imx6ull-somdevices.dtb: Added WiFi WILC1000 fuinctionallity.
authorJosep Orga <jorga@somdevices.com>
Wed, 27 Nov 2019 12:26:03 +0000 (13:26 +0100)
committerJosep Orga <jorga@somdevices.com>
Wed, 27 Nov 2019 12:26:03 +0000 (13:26 +0100)
wilc1000: Added drivers.
TODO: Check all functionallity and fix this warning related to regs:

Warning (reg_format): "reg" property in /soc/aips-bus@02100000/usdhc@02190000/wilc_sdio@0 has invalid length (4 bytes) (#address-cells == 2, #size-cells == 1)
Warning (avoid_default_addr_size): Relying on default #address-cells value for /soc/aips-bus@02100000/usdhc@02190000/wilc_sdio@0
Warning (avoid_default_addr_size): Relying on default #size-cells value for /soc/aips-bus@02100000/usdhc@02190000/wilc_sdio@0

Signed-off-by: Josep Orga <jorga@somdevices.com>
26 files changed:
arch/arm/boot/dts/imx6ull-somdevices.dts
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/mchp/Kconfig [new file with mode: 0644]
drivers/net/wireless/mchp/Makefile [new file with mode: 0644]
drivers/net/wireless/mchp/host_interface.c [new file with mode: 0644]
drivers/net/wireless/mchp/host_interface.h [new file with mode: 0644]
drivers/net/wireless/mchp/microchip,wilc,sdio.txt [new file with mode: 0644]
drivers/net/wireless/mchp/microchip,wilc,spi.txt [new file with mode: 0644]
drivers/net/wireless/mchp/sysfs.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_bt.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_debugfs.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_debugfs.h [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_mon.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_netdev.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_netdev.h [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_sdio.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_spi.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wfi_cfgoperations.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wfi_cfgoperations.h [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wfi_netdevice.h [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wlan.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wlan.h [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wlan_cfg.c [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wlan_cfg.h [new file with mode: 0644]
drivers/net/wireless/mchp/wilc_wlan_if.h [new file with mode: 0644]

index 14ca231..a380139 100644 (file)
                        gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;
                        states = <1300000 0x1 1400000 0x0>;
                };
+
+               reg_wlan_en: regulator@10 {
+                       compatible = "regulator-fixed";
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&pinctrl_wifi_en>;
+                       regulator-name = "wlan";
+                       regulator-min-microvolt = <3300000>;
+                       regulator-max-microvolt = <3300000>;
+                       regulator-boot-on;
+                       gpios = <&gpio5 7 0>;
+                       startup-delay-us = <70000>;
+               };
        };
 
        spi4 {
                                 MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04   0x17059
                         >;
                 };
-        };
+               pinctrl_wifi_en: pinctrlwifi {
+                                       fsl,pins = <
+                                                       MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06     0x10071
+                                                       MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07     0x10071
+                                       >;
+                       };
+               };
 };
 
 
 };
 
 &usdhc1 {
-       pinctrl-names = "default", "state_100mhz", "state_200mhz";
-       pinctrl-0 = <&pinctrl_usdhc1>;
-       pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
-       pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
-       cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
-       keep-power-in-suspend;
-       enable-sdio-wakeup;
-       vmmc-supply = <&reg_sd1_vmmc>;
-       status = "okay";
+    /*pinctrl-names = "default", "state_100mhz", "state_200mhz";
+    pinctrl-0 = <&pinctrl_usdhc1>;
+    pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
+    pinctrl-2 = <&pinctrl_usdhc1_200mhz>;*/
+    pinctrl-names = "default";
+    pinctrl-0 = <&pinctrl_usdhc1>;
+    //cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
+    //no-1-8-v;
+    keep-power-in-suspend;
+    enable-sdio-wakeup;
+    non-removable;
+    wifi-host;
+    //vmmc-supply = <&reg_sd1_vmmc>;
+    //vmmc-supply = <&reg_wlan_en>;
+    bus-width = <4>;
+    status = "okay";
+    wilc_sdio: wilc_sdio@0{
+        compatible = "microchip,wilc1000";
+        pinctrl-0 = <&pinctrl_wifi_en>;
+        status = "okay";
+                //interrupts = <5 IRQ_TYPE_EDGE_RISING>;
+                //gpio_irq = <&gpio5 5 0>;
+        gpio_reset = <&gpio5 7 0>;
+        //reset-gpios = <&gpio5 7 0>;
+        gpio_chip_en = <&gpio5 6 0>;
+        //chip_en-gpios = <&gpio5 6 0>;
+        reg = <0>;
+        bus-width = <4>;
+    };
 };
 
 &usdhc2 {
index 591686b..563e44b 100644 (file)
@@ -34,6 +34,7 @@ source "drivers/net/wireless/rsi/Kconfig"
 source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
+source "drivers/net/wireless/mchp/Kconfig"
 
 config PCMCIA_RAYCS
        tristate "Aviator/Raytheon 2.4GHz wireless support"
index c2e5add..29deca0 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
 obj-$(CONFIG_WLAN_VENDOR_ST) += st/
 obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
 obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
+obj-$(CONFIG_WLAN_VENDOR_MCHP) += mchp/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)     += ray_cs.o
diff --git a/drivers/net/wireless/mchp/Kconfig b/drivers/net/wireless/mchp/Kconfig
new file mode 100644 (file)
index 0000000..05828d6
--- /dev/null
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0
+config WILC
+       tristate
+
+config WLAN_VENDOR_MCHP
+        bool "Microhip devices"
+        default y
+       help
+       This adds support for WILC1000 & WILC3000 chips which support
+       IEEE 802.11n WiFi. WILC3000 is a WiFi-BT combo chip.
+
+if WLAN_VENDOR_MCHP
+
+config WILC_SDIO
+       tristate "WILC SDIO"
+       depends on CFG80211 && INET && MMC
+       select WILC
+       help
+         This module adds support for the SDIO interface of adapters using
+         WILC1000 & WILC3000 chipset. The Atmel WILC1000 SDIO is a full speed interface.
+         It meets SDIO card specification version 2.0. The interface supports
+         the 1-bit/4-bit SD transfer mode at the clock range of 0-50 MHz.
+         The host can use this interface to read and write from any register
+         within the chip as well as configure the WILC1000 for data DMA.
+         To use this interface, pin9 (SDIO_SPI_CFG) must be grounded. Select
+         this if your platform is using the SDIO bus.
+         WILC3000 additionally supports BT 4.0 and BLE modes.
+
+config WILC_SPI
+       tristate "WILC SPI"
+       depends on CFG80211 && INET && SPI
+       select WILC
+       help
+         This module adds support for the SPI interface of adapters using
+         WILC1000 & WILC3000 chipset. The Atmel WILC1000 has a Serial Peripheral
+         Interface (SPI) that operates as a SPI slave. This SPI interface can
+         be used for control and for serial I/O of 802.11 data. The SPI is a
+         full-duplex slave synchronous serial interface that is available
+         immediately following reset when pin 9 (SDIO_SPI_CFG) is tied to
+         VDDIO. Select this if your platform is using the SPI bus.
+         WILC3000 additionally supports BT 4.0 and BLE modes.
+
+config WILC_HW_OOB_INTR
+       bool "WILC out of band interrupt"
+       depends on WILC_SDIO
+       default n
+       help
+         This option enables out-of-band interrupt support for the WILC1000 &
+         WILC3000 chipset. This OOB interrupt is intended to provide a faster
+         interrupt mechanism for SDIO host controllers that don't support SDIO
+         interrupt. Select this option If the SDIO host controller in your
+         platform doesn't support SDIO time devision interrupt.
+endif
diff --git a/drivers/net/wireless/mchp/Makefile b/drivers/net/wireless/mchp/Makefile
new file mode 100644 (file)
index 0000000..9f440b5
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(src)/ -DWILC_ASIC_A0 -DWILC_DEBUGFS
+ccflags-y += -DDISABLE_PWRSAVE_AND_SCAN_DURING_IP
+
+wilc-objs := wilc_wfi_cfgoperations.o wilc_netdev.o wilc_mon.o \
+                       host_interface.o wilc_wlan_cfg.o wilc_debugfs.o \
+                       wilc_wlan.o sysfs.o wilc_bt.o
+
+obj-$(CONFIG_WILC_SDIO) += wilc-sdio.o
+wilc-sdio-objs += $(wilc-objs)
+wilc-sdio-objs += wilc_sdio.o
+
+obj-$(CONFIG_WILC_SPI) += wilc-spi.o
+wilc-spi-objs += $(wilc-objs)
+wilc-spi-objs += wilc_spi.o 
diff --git a/drivers/net/wireless/mchp/host_interface.c b/drivers/net/wireless/mchp/host_interface.c
new file mode 100644 (file)
index 0000000..c8eba4f
--- /dev/null
@@ -0,0 +1,2769 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_netdev.h"
+#include "wilc_wfi_cfgoperations.h"
+
+#define WILC_HIF_SCAN_TIMEOUT_MS                    5000
+#define WILC_HIF_CONNECT_TIMEOUT_MS                 9500
+
+#define WILC_FALSE_FRMWR_CHANNEL                   100
+#define WILC_MAX_RATES_SUPPORTED                   12
+
+/* Generic success will return 0 */
+#define WILC_SUCCESS           0       /* Generic success */
+
+/* Negative numbers to indicate failures */
+/* Generic Fail */
+#define        WILC_FAIL               -100
+/* Busy with another operation*/
+#define        WILC_BUSY               -101
+/* A given argument is invalid*/
+#define        WILC_INVALID_ARGUMENT   -102
+/* An API request would violate the Driver state machine
+ * (i.e. to start PID while not camped)
+ */
+#define        WILC_INVALID_STATE      -103
+/* In copy operations if the copied data is larger than the allocated buffer*/
+#define        WILC_BUFFER_OVERFLOW    -104
+/* null pointer is passed or used */
+#define WILC_NULL_PTR          -105
+#define        WILC_EMPTY              -107
+#define WILC_FULL              -108
+#define        WILC_TIMEOUT            -109
+/* The required operation have been canceled by the user*/
+#define WILC_CANCELED          -110
+/* The Loaded file is corruped or having an invalid format */
+#define WILC_INVALID_FILE      -112
+/* Cant find the file to load */
+#define WILC_NOT_FOUND         -113
+#define WILC_NO_MEM            -114
+#define WILC_UNSUPPORTED_VERSION -115
+#define WILC_FILE_EOF          -116
+
+#if KERNEL_VERSION(3, 17, 0) > LINUX_VERSION_CODE
+struct ieee80211_wmm_ac_param {
+       u8 aci_aifsn; /* AIFSN, ACM, ACI */
+       u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+       __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+       u8 element_id; /* Element ID: 221 (0xdd); */
+       u8 len; /* Length: 24 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 1 */
+       u8 version; /* 1 for WMM version 1.0 */
+       u8 qos_info; /* AP/STA specific QoS info */
+       u8 reserved; /* 0 */
+       /* AC_BE, AC_BK, AC_VI, AC_VO */
+       struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+#endif
+
+struct send_buffered_eap {
+       void (*deliver_to_stack)(struct wilc_vif *vif, u8 *buff, u32 size,
+                             u32 pkt_offset, u8 status);
+       void (*eap_buf_param)(void *priv);
+       u8 *buff;
+       unsigned int size;
+       unsigned int pkt_offset;
+       void *user_arg;
+};
+
+struct wilc_rcvd_mac_info {
+       u8 status;
+};
+
+struct wilc_set_multicast {
+       u32 enabled;
+       u32 cnt;
+       u8 *mc_list;
+};
+
+struct host_if_wowlan_trigger {
+       u8 wowlan_trigger;
+};
+
+struct bt_coex_mode {
+       u8 bt_coex;
+};
+
+struct host_if_set_ant {
+       u8 mode;
+       u8 antenna1;
+       u8 antenna2;
+       u8 gpio_mode;
+};
+
+struct wilc_del_all_sta {
+       u8 assoc_sta;
+       u8 mac[WILC_MAX_NUM_STA][ETH_ALEN];
+};
+
+struct wilc_op_mode {
+       __le32 mode;
+};
+
+struct wilc_reg_frame {
+       bool reg;
+       u8 reg_id;
+       __le16 frame_type;
+} __packed;
+
+struct wilc_drv_handler {
+       __le32 handler;
+       u8 mode;
+} __packed;
+
+struct wilc_wep_key {
+       u8 index;
+       u8 key_len;
+       u8 key[0];
+} __packed;
+
+struct wilc_sta_wpa_ptk {
+       u8 mac_addr[ETH_ALEN];
+       u8 key_len;
+       u8 key[0];
+} __packed;
+
+struct wilc_ap_wpa_ptk {
+       u8 mac_addr[ETH_ALEN];
+       u8 index;
+       u8 key_len;
+       u8 key[0];
+} __packed;
+
+struct wilc_gtk_key {
+       u8 mac_addr[ETH_ALEN];
+       u8 rsc[8];
+       u8 index;
+       u8 key_len;
+       u8 key[0];
+} __packed;
+
+union wilc_message_body {
+       struct wilc_rcvd_net_info net_info;
+       struct wilc_rcvd_mac_info mac_info;
+       struct wilc_set_multicast mc_info;
+       struct wilc_remain_ch remain_on_ch;
+       char *data;
+       struct send_buffered_eap send_buff_eap;
+       struct host_if_set_ant set_ant;
+       struct host_if_wowlan_trigger wow_trigger;
+       struct bt_coex_mode bt_coex_mode;
+};
+
+struct host_if_msg {
+       union wilc_message_body body;
+       struct wilc_vif *vif;
+       struct work_struct work;
+       void (*fn)(struct work_struct *ws);
+       struct completion work_comp;
+       bool is_sync;
+};
+
+struct wilc_noa_opp_enable {
+       u8 ct_window;
+       u8 cnt;
+       __le32 duration;
+       __le32 interval;
+       __le32 start_time;
+} __packed;
+
+struct wilc_noa_opp_disable {
+       u8 cnt;
+       __le32 duration;
+       __le32 interval;
+       __le32 start_time;
+} __packed;
+
+struct wilc_join_bss_param {
+       char ssid[IEEE80211_MAX_SSID_LEN];
+       u8 ssid_terminator;
+       u8 bss_type;
+       u8 ch;
+       __le16 cap_info;
+       u8 sa[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+       __le16 beacon_period;
+       u8 dtim_period;
+       u8 supp_rates[WILC_MAX_RATES_SUPPORTED + 1];
+       u8 wmm_cap;
+       u8 uapsd_cap;
+       u8 ht_capable;
+       u8 rsn_found;
+       u8 rsn_grp_policy;
+       u8 mode_802_11i;
+       u8 p_suites[3];
+       u8 akm_suites[3];
+       u8 rsn_cap[2];
+       u8 noa_enabled;
+       __le32 tsf_lo;
+       u8 idx;
+       u8 opp_enabled;
+       union {
+               struct wilc_noa_opp_disable opp_dis;
+               struct wilc_noa_opp_enable opp_en;
+       };
+} __packed;
+
+/* 'msg' should be free by the caller for syc */
+static struct host_if_msg*
+wilc_alloc_work(struct wilc_vif *vif, void (*work_fun)(struct work_struct *),
+               bool is_sync)
+{
+       struct host_if_msg *msg;
+
+       if (!work_fun)
+               return ERR_PTR(-EINVAL);
+
+       msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
+       if (!msg)
+               return ERR_PTR(-ENOMEM);
+       msg->fn = work_fun;
+       msg->vif = vif;
+       msg->is_sync = is_sync;
+       if (is_sync)
+               init_completion(&msg->work_comp);
+
+       return msg;
+}
+
+static int wilc_enqueue_work(struct host_if_msg *msg)
+{
+       INIT_WORK(&msg->work, msg->fn);
+
+       if (!msg->vif || !msg->vif->wilc || !msg->vif->wilc->hif_workqueue)
+               return -EINVAL;
+
+       if (!queue_work(msg->vif->wilc->hif_workqueue, &msg->work))
+               return -EINVAL;
+
+       return 0;
+}
+
+/* The idx starts from 0 to (NUM_CONCURRENT_IFC - 1), but 0 index used as
+ * special purpose in wilc device, so we add 1 to the index to starts from 1.
+ * As a result, the returned index will be 1 to NUM_CONCURRENT_IFC.
+ */
+int wilc_get_vif_idx(struct wilc_vif *vif)
+{
+       return vif->idx + 1;
+}
+
+/* We need to minus 1 from idx which is from wilc device to get real index
+ * of wilc->vif[], because we add 1 when pass to wilc device in the function
+ * wilc_get_vif_idx.
+ * As a result, the index should be between 0 and (NUM_CONCURRENT_IFC - 1).
+ */
+static struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx)
+{
+       int index = idx - 1;
+
+       if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC)
+               return NULL;
+
+       return wilc->vif[index];
+}
+
+static void handle_send_buffered_eap(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+       struct send_buffered_eap *hif_buff_eap = &msg->body.send_buff_eap;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending bufferd eapol to WPAS\n");
+       if (!hif_buff_eap->buff)
+               goto out;
+
+       if (hif_buff_eap->deliver_to_stack)
+               hif_buff_eap->deliver_to_stack(vif, hif_buff_eap->buff,
+                                              hif_buff_eap->size,
+                                              hif_buff_eap->pkt_offset,
+                                              PKT_STATUS_BUFFERED);
+       if (hif_buff_eap->eap_buf_param)
+               hif_buff_eap->eap_buf_param(hif_buff_eap->user_arg);
+
+       if (hif_buff_eap->buff != NULL) {
+               kfree(hif_buff_eap->buff);
+               hif_buff_eap->buff = NULL;
+       }
+
+out:
+       kfree(msg);
+}
+
+int wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type,
+             u8 *ch_freq_list, u8 ch_list_len,
+             void (*scan_result_fn)(enum scan_event,
+                                    struct wilc_rcvd_net_info *, void *),
+             void *user_arg, struct cfg80211_scan_request *request)
+{
+       int result = 0;
+       struct wid wid_list[5];
+       u32 index = 0;
+       u32 i, scan_timeout;
+       u8 *buffer;
+       u8 valuesize = 0;
+       u8 *search_ssid_vals = NULL;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+                                                             WILC_P2P_IFC);
+       struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+                                                              WILC_WLAN_IFC);
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setting SCAN params\n");
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Scanning: In [%d] state\n",
+                  hif_drv->hif_state);
+
+       if (hif_drv_p2p != NULL) {
+               if (hif_drv_p2p->hif_state != HOST_IF_IDLE &&
+                   hif_drv_p2p->hif_state != HOST_IF_CONNECTED) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Don't scan. P2P_IFC is in state [%d]\n",
+                                  hif_drv_p2p->hif_state);
+                       result = -EBUSY;
+                       goto error;
+               }
+       }
+
+       if (hif_drv_wlan != NULL) {
+               if (hif_drv_wlan->hif_state != HOST_IF_IDLE &&
+                   hif_drv_wlan->hif_state != HOST_IF_CONNECTED) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Don't scan. WLAN_IFC is in state [%d]\n",
+                                  hif_drv_wlan->hif_state);
+                       result = -EBUSY;
+                       goto error;
+               }
+       }
+       if (vif->connecting) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Don't do scan in (CONNECTING) state\n");
+               result = -EBUSY;
+               goto error;
+       }
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       if (vif->obtaining_ip) {
+               PRINT_ER(vif->ndev, "Don't do obss scan\n");
+               result = -EBUSY;
+               goto error;
+       }
+#endif
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setting SCAN params\n");
+       hif_drv->usr_scan_req.ch_cnt = 0;
+
+       if (request->n_ssids) {
+               for (i = 0; i < request->n_ssids; i++)
+                       valuesize += ((request->ssids[i].ssid_len) + 1);
+               search_ssid_vals = kmalloc(valuesize + 1, GFP_KERNEL);
+               if (search_ssid_vals) {
+                       wid_list[index].id = WID_SSID_PROBE_REQ;
+                       wid_list[index].type = WID_STR;
+                       wid_list[index].val = search_ssid_vals;
+                       buffer = wid_list[index].val;
+
+                       *buffer++ = request->n_ssids;
+
+               PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                          "In Handle_ProbeRequest number of ssid %d\n",
+                        request->n_ssids);
+                       for (i = 0; i < request->n_ssids; i++) {
+                               *buffer++ = request->ssids[i].ssid_len;
+                               memcpy(buffer, request->ssids[i].ssid,
+                                      request->ssids[i].ssid_len);
+                               buffer += request->ssids[i].ssid_len;
+                       }
+                       wid_list[index].size = (s32)(valuesize + 1);
+                       index++;
+               }
+       }
+
+       wid_list[index].id = WID_INFO_ELEMENT_PROBE;
+       wid_list[index].type = WID_BIN_DATA;
+       wid_list[index].val = (s8 *)request->ie;
+       wid_list[index].size = request->ie_len;
+       index++;
+
+       wid_list[index].id = WID_SCAN_TYPE;
+       wid_list[index].type = WID_CHAR;
+       wid_list[index].size = sizeof(char);
+       wid_list[index].val = (s8 *)&scan_type;
+       index++;
+
+#if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE
+       scan_timeout = WILC_HIF_SCAN_TIMEOUT_MS;
+#else
+       if (scan_type == WILC_FW_PASSIVE_SCAN && request->duration) {
+               wid_list[index].id = WID_PASSIVE_SCAN_TIME;
+               wid_list[index].type = WID_SHORT;
+               wid_list[index].size = sizeof(u16);
+               wid_list[index].val = (s8 *)&request->duration;
+               index++;
+
+               scan_timeout = (request->duration * ch_list_len) + 500;
+       } else {
+               scan_timeout = WILC_HIF_SCAN_TIMEOUT_MS;
+       }
+#endif
+       wid_list[index].id = WID_SCAN_CHANNEL_LIST;
+       wid_list[index].type = WID_BIN_DATA;
+
+       if (ch_freq_list && ch_list_len > 0) {
+               for (i = 0; i < ch_list_len; i++) {
+                       if (ch_freq_list[i] > 0)
+                               ch_freq_list[i] -= 1;
+               }
+       }
+
+       wid_list[index].val = ch_freq_list;
+       wid_list[index].size = ch_list_len;
+       index++;
+
+       wid_list[index].id = WID_START_SCAN_REQ;
+       wid_list[index].type = WID_CHAR;
+       wid_list[index].size = sizeof(char);
+       wid_list[index].val = (s8 *)&scan_source;
+       index++;
+
+       hif_drv->usr_scan_req.scan_result = scan_result_fn;
+       hif_drv->usr_scan_req.arg = user_arg;
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+                                     index,
+                                     wilc_get_vif_idx(vif));
+       if (result) {
+               PRINT_ER(vif->ndev, "Failed to send scan parameters\n");
+               goto error;
+       } else {
+               hif_drv->scan_timer_vif = vif;
+               PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                          ">> Starting the SCAN timer\n");
+#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+               hif_drv->scan_timer.data = (unsigned long)hif_drv;
+#endif
+               mod_timer(&hif_drv->scan_timer,
+                         jiffies + msecs_to_jiffies(scan_timeout));
+       }
+
+error:
+
+       kfree(search_ssid_vals);
+
+       return result;
+}
+
+s32 handle_scan_done(struct wilc_vif *vif, enum scan_event evt)
+{
+       s32 result = 0;
+       u8 abort_running_scan;
+       struct wid wid;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct wilc_user_scan_req *scan_req;
+       u8 null_bssid[6] = {0};
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "handling scan done\n");
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               return result;
+       }
+
+       if (evt == SCAN_EVENT_DONE) {
+               if (memcmp(hif_drv->assoc_bssid, null_bssid, ETH_ALEN) == 0)
+                       hif_drv->hif_state = HOST_IF_IDLE;
+               else
+                       hif_drv->hif_state = HOST_IF_CONNECTED;
+       } else if (evt == SCAN_EVENT_ABORTED) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG, "Abort running scan\n");
+               abort_running_scan = 1;
+               wid.id = WID_ABORT_RUNNING_SCAN;
+               wid.type = WID_CHAR;
+               wid.val = (s8 *)&abort_running_scan;
+               wid.size = sizeof(char);
+
+               result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                             wilc_get_vif_idx(vif));
+
+               if (result) {
+                       PRINT_ER(vif->ndev, "Failed to set abort running\n");
+                       result = -EFAULT;
+               }
+       }
+
+       scan_req = &hif_drv->usr_scan_req;
+       if (scan_req->scan_result) {
+               scan_req->scan_result(evt, NULL, scan_req->arg);
+               scan_req->scan_result = NULL;
+       }
+
+       return result;
+}
+
+static int wilc_send_connect_wid(struct wilc_vif *vif)
+{
+       int result = 0;
+       struct wid wid_list[4];
+       u32 wid_cnt = 0;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct wilc_conn_info *conn_attr = &hif_drv->conn_info;
+       struct wilc_join_bss_param *bss_param = hif_drv->conn_info.param;
+       struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+                                                             WILC_P2P_IFC);
+       struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+                                                              WILC_WLAN_IFC);
+
+       if (hif_drv_p2p != NULL) {
+               if (hif_drv_p2p->hif_state == HOST_IF_SCANNING) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Don't scan. P2P_IFC is in state [%d]\n",
+                        hif_drv_p2p->hif_state);
+                        result = -EFAULT;
+                       goto error;
+               }
+       }
+       if (hif_drv_wlan != NULL) {
+               if (hif_drv_wlan->hif_state == HOST_IF_SCANNING) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Don't scan. WLAN_IFC is in state [%d]\n",
+                        hif_drv_wlan->hif_state);
+                       result = -EFAULT;
+                       goto error;
+               }
+       }
+
+       wid_list[wid_cnt].id = WID_INFO_ELEMENT_ASSOCIATE;
+       wid_list[wid_cnt].type = WID_BIN_DATA;
+       wid_list[wid_cnt].val = conn_attr->req_ies;
+       wid_list[wid_cnt].size = conn_attr->req_ies_len;
+       wid_cnt++;
+
+       wid_list[wid_cnt].id = WID_11I_MODE;
+       wid_list[wid_cnt].type = WID_CHAR;
+       wid_list[wid_cnt].size = sizeof(char);
+       wid_list[wid_cnt].val = (s8 *)&conn_attr->security;
+       wid_cnt++;
+
+       PRINT_D(vif->ndev, HOSTINF_DBG, "Encrypt Mode = %x\n",
+               conn_attr->security);
+       wid_list[wid_cnt].id = WID_AUTH_TYPE;
+       wid_list[wid_cnt].type = WID_CHAR;
+       wid_list[wid_cnt].size = sizeof(char);
+       wid_list[wid_cnt].val = (s8 *)&conn_attr->auth_type;
+       wid_cnt++;
+
+       PRINT_D(vif->ndev, HOSTINF_DBG, "Authentication Type = %x\n",
+               conn_attr->auth_type);
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Connecting to network on channel %d\n", conn_attr->ch);
+
+       wid_list[wid_cnt].id = WID_JOIN_REQ_EXTENDED;
+       wid_list[wid_cnt].type = WID_STR;
+       wid_list[wid_cnt].size = sizeof(*bss_param);
+       wid_list[wid_cnt].val = (u8 *)bss_param;
+       wid_cnt++;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "send HOST_IF_WAITING_CONN_RESP\n");
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+                                     wid_cnt,
+                                     wilc_get_vif_idx(vif));
+       if (result) {
+               PRINT_ER(vif->ndev, "failed to send config packet\n");
+               goto error;
+       } else {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "set HOST_IF_WAITING_CONN_RESP\n");
+               hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP;
+       }
+
+       return 0;
+
+error:
+
+       kfree(conn_attr->req_ies);
+       conn_attr->req_ies = NULL;
+
+       return result;
+}
+
+static void handle_connect_timeout(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+       int result;
+       struct wid wid;
+       u16 dummy_reason_code = 0;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               goto out;
+       }
+
+       hif_drv->hif_state = HOST_IF_IDLE;
+
+       if (hif_drv->conn_info.conn_result) {
+               hif_drv->conn_info.conn_result(EVENT_CONN_RESP,
+                                              WILC_MAC_STATUS_DISCONNECTED,
+                                              hif_drv->conn_info.arg);
+
+       } else {
+               PRINT_ER(vif->ndev, "conn_result is NULL\n");
+       }
+
+       wid.id = WID_DISCONNECT;
+       wid.type = WID_CHAR;
+       wid.val = (s8 *)&dummy_reason_code;
+       wid.size = sizeof(char);
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending disconnect request\n");
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send disconect\n");
+
+       hif_drv->conn_info.req_ies_len = 0;
+       kfree(hif_drv->conn_info.req_ies);
+       hif_drv->conn_info.req_ies = NULL;
+
+out:
+       kfree(msg);
+}
+
+void *wilc_parse_join_bss_param(struct cfg80211_bss *bss,
+                               struct cfg80211_crypto_settings *crypto)
+{
+       struct wilc_join_bss_param *param;
+       struct ieee80211_p2p_noa_attr noa_attr;
+       u8 rates_len = 0;
+       const u8 *tim_elm, *ssid_elm, *rates_ie, *supp_rates_ie;
+       const u8 *ht_ie, *wpa_ie, *wmm_ie, *rsn_ie;
+       int ret;
+       const struct cfg80211_bss_ies *ies = rcu_dereference(bss->ies);
+
+       param = kzalloc(sizeof(*param), GFP_KERNEL);
+       if (!param)
+               return NULL;
+
+       param->beacon_period = cpu_to_le16(bss->beacon_interval);
+       param->cap_info = cpu_to_le16(bss->capability);
+       param->bss_type = WILC_FW_BSS_TYPE_INFRA;
+       param->ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+       ether_addr_copy(param->bssid, bss->bssid);
+
+       ssid_elm = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+       if (ssid_elm) {
+               if (ssid_elm[1] <= IEEE80211_MAX_SSID_LEN)
+                       memcpy(param->ssid, ssid_elm + 2, ssid_elm[1]);
+       }
+
+       tim_elm = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len);
+       if (tim_elm && tim_elm[1] >= 2)
+               param->dtim_period = tim_elm[3];
+
+       memset(param->p_suites, 0xFF, 3);
+       memset(param->akm_suites, 0xFF, 3);
+
+       rates_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, ies->data, ies->len);
+       if (rates_ie) {
+               rates_len = rates_ie[1];
+               param->supp_rates[0] = rates_len;
+               memcpy(&param->supp_rates[1], rates_ie + 2, rates_len);
+       }
+
+       supp_rates_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, ies->data,
+                                        ies->len);
+       if (supp_rates_ie) {
+               if (supp_rates_ie[1] > (WILC_MAX_RATES_SUPPORTED - rates_len))
+                       param->supp_rates[0] = WILC_MAX_RATES_SUPPORTED;
+               else
+                       param->supp_rates[0] += supp_rates_ie[1];
+
+               memcpy(&param->supp_rates[rates_len + 1], supp_rates_ie + 2,
+                      (param->supp_rates[0] - rates_len));
+       }
+
+       ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies->data, ies->len);
+       if (ht_ie)
+               param->ht_capable = true;
+
+       ret = cfg80211_get_p2p_attr(ies->data, ies->len,
+                                   IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+                                   (u8 *)&noa_attr, sizeof(noa_attr));
+       if (ret > 0) {
+               param->tsf_lo = cpu_to_le32(ies->tsf);
+               param->noa_enabled = 1;
+               param->idx = noa_attr.index;
+               if (noa_attr.oppps_ctwindow & IEEE80211_P2P_OPPPS_ENABLE_BIT) {
+                       param->opp_enabled = 1;
+                       param->opp_en.ct_window = noa_attr.oppps_ctwindow;
+                       param->opp_en.cnt = noa_attr.desc[0].count;
+                       param->opp_en.duration = noa_attr.desc[0].duration;
+                       param->opp_en.interval = noa_attr.desc[0].interval;
+                       param->opp_en.start_time = noa_attr.desc[0].start_time;
+               } else {
+                       param->opp_enabled = 0;
+                       param->opp_dis.cnt = noa_attr.desc[0].count;
+                       param->opp_dis.duration = noa_attr.desc[0].duration;
+                       param->opp_dis.interval = noa_attr.desc[0].interval;
+                       param->opp_dis.start_time = noa_attr.desc[0].start_time;
+               }
+       }
+       wmm_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                        WLAN_OUI_TYPE_MICROSOFT_WMM,
+                                        ies->data, ies->len);
+       if (wmm_ie) {
+               struct ieee80211_wmm_param_ie *ie;
+
+               ie = (struct ieee80211_wmm_param_ie *)wmm_ie;
+               if ((ie->oui_subtype == 0 || ie->oui_subtype == 1) &&
+                   ie->version == 1) {
+                       param->wmm_cap = true;
+                       if (ie->qos_info & BIT(7))
+                               param->uapsd_cap = true;
+               }
+       }
+
+       wpa_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                        WLAN_OUI_TYPE_MICROSOFT_WPA,
+                                        ies->data, ies->len);
+       if (wpa_ie) {
+               param->mode_802_11i = 1;
+               param->rsn_found = true;
+       }
+
+       rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, ies->data, ies->len);
+       if (rsn_ie) {
+               int offset = 8;
+
+               param->mode_802_11i = 2;
+               param->rsn_found = true;
+               //extract RSN capabilities
+               offset += (rsn_ie[offset] * 4) + 2;
+               offset += (rsn_ie[offset] * 4) + 2;
+               memcpy(param->rsn_cap, &rsn_ie[offset], 2);
+       }
+
+       if (param->rsn_found) {
+               int i;
+
+               param->rsn_grp_policy = crypto->cipher_group & 0xFF;
+               for (i = 0; i < crypto->n_ciphers_pairwise && i < 3; i++)
+                       param->p_suites[i] = crypto->ciphers_pairwise[i] & 0xFF;
+
+               for (i = 0; i < crypto->n_akm_suites && i < 3; i++)
+                       param->akm_suites[i] = crypto->akm_suites[i] & 0xFF;
+       }
+
+       return (void *)param;
+}
+
+static void handle_rcvd_ntwrk_info(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_rcvd_net_info *rcvd_info = &msg->body.net_info;
+       struct wilc_user_scan_req *scan_req = &msg->vif->hif_drv->usr_scan_req;
+       const u8 *ch_elm;
+       u8 *ies;
+       int ies_len;
+       size_t offset;
+
+       PRINT_D(msg->vif->ndev, HOSTINF_DBG,
+               "Handling received network info\n");
+
+       if (ieee80211_is_probe_resp(rcvd_info->mgmt->frame_control))
+               offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       else if (ieee80211_is_beacon(rcvd_info->mgmt->frame_control))
+               offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+       else
+               goto done;
+
+       ies = rcvd_info->mgmt->u.beacon.variable;
+       ies_len = rcvd_info->frame_len - offset;
+       if (ies_len <= 0)
+               goto done;
+
+       PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "New network found\n");
+       /* extract the channel from recevied mgmt frame */
+       ch_elm = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ies, ies_len);
+       if (ch_elm && ch_elm[1] > 0)
+               rcvd_info->ch = ch_elm[2];
+
+       if (scan_req->scan_result)
+               scan_req->scan_result(SCAN_EVENT_NETWORK_FOUND,
+                                     rcvd_info, scan_req->arg);
+
+done:
+       kfree(rcvd_info->mgmt);
+       kfree(msg);
+}
+
+static void host_int_get_assoc_res_info(struct wilc_vif *vif,
+                                       u8 *assoc_resp_info,
+                                       u32 max_assoc_resp_info_len,
+                                       u32 *rcvd_assoc_resp_info_len)
+{
+       int result;
+       struct wid wid;
+
+       wid.id = WID_ASSOC_RES_INFO;
+       wid.type = WID_STR;
+       wid.val = assoc_resp_info;
+       wid.size = max_assoc_resp_info_len;
+
+       result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result) {
+               *rcvd_assoc_resp_info_len = 0;
+               PRINT_ER(vif->ndev, "Failed to send association response\n");
+               return;
+       }
+
+       *rcvd_assoc_resp_info_len = wid.size;
+}
+
+static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len,
+                                     struct wilc_conn_info *ret_conn_info)
+{
+       u8 *ies;
+       u16 ies_len;
+       struct assoc_resp *res = (struct assoc_resp *)buffer;
+
+       ret_conn_info->status = le16_to_cpu(res->status_code);
+       if (ret_conn_info->status == WLAN_STATUS_SUCCESS) {
+               ies = &buffer[sizeof(*res)];
+               ies_len = buffer_len - sizeof(*res);
+
+               ret_conn_info->resp_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+               if (!ret_conn_info->resp_ies)
+                       return -ENOMEM;
+
+               ret_conn_info->resp_ies_len = ies_len;
+       }
+
+       return 0;
+}
+
+static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif,
+                                                 u8 mac_status)
+{
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct wilc_conn_info *conn_info = &hif_drv->conn_info;
+
+       if (mac_status == WILC_MAC_STATUS_CONNECTED) {
+               u32 assoc_resp_info_len;
+
+               memset(hif_drv->assoc_resp, 0, WILC_MAX_ASSOC_RESP_FRAME_SIZE);
+
+               host_int_get_assoc_res_info(vif, hif_drv->assoc_resp,
+                                           WILC_MAX_ASSOC_RESP_FRAME_SIZE,
+                                           &assoc_resp_info_len);
+
+               PRINT_D(vif->ndev, HOSTINF_DBG,
+                       "Received association response = %d\n",
+                       assoc_resp_info_len);
+               if (assoc_resp_info_len != 0) {
+                       s32 err = 0;
+
+                       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                                  "Parsing association response\n");
+                       err = wilc_parse_assoc_resp_info(hif_drv->assoc_resp,
+                                                        assoc_resp_info_len,
+                                                        conn_info);
+                       if (err)
+                               PRINT_ER(vif->ndev,
+                                        "wilc_parse_assoc_resp_info() returned error %d\n",
+                                        err);
+               }
+       }
+
+       del_timer(&hif_drv->connect_timer);
+       conn_info->conn_result(EVENT_CONN_RESP, mac_status, conn_info->arg);
+
+       if (mac_status == WILC_MAC_STATUS_CONNECTED &&
+           conn_info->status == WLAN_STATUS_SUCCESS) {
+               PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                          "MAC status : CONNECTED and Connect Status : Successful\n");
+               hif_drv->hif_state = HOST_IF_CONNECTED;
+               ether_addr_copy(hif_drv->assoc_bssid, conn_info->bssid);
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+               handle_pwrsave_for_IP(vif, IP_STATE_OBTAINING);
+#endif
+       } else {
+               PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                          "MAC status : %d and Connect Status : %d\n",
+                          mac_status, conn_info->status);
+               hif_drv->hif_state = HOST_IF_IDLE;
+       }
+
+       kfree(conn_info->resp_ies);
+       conn_info->resp_ies = NULL;
+       conn_info->resp_ies_len = 0;
+
+       kfree(conn_info->req_ies);
+       conn_info->req_ies = NULL;
+       conn_info->req_ies_len = 0;
+}
+
+static inline void host_int_handle_disconnect(struct wilc_vif *vif)
+{
+       struct host_if_drv *hif_drv = vif->hif_drv;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Received WILC_MAC_STATUS_DISCONNECTED from the FW\n");
+       if (hif_drv->usr_scan_req.scan_result) {
+               PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                          "\n\n<< Abort the running OBSS Scan >>\n\n");
+               del_timer(&hif_drv->scan_timer);
+               handle_scan_done(vif, SCAN_EVENT_ABORTED);
+       }
+
+       if (hif_drv->conn_info.conn_result) {
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+               handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+               hif_drv->conn_info.conn_result(EVENT_DISCONN_NOTIF,
+                                              0, hif_drv->conn_info.arg);
+       } else {
+               PRINT_ER(vif->ndev, "Connect result NULL\n");
+       }
+
+       eth_zero_addr(hif_drv->assoc_bssid);
+
+       hif_drv->conn_info.req_ies_len = 0;
+       kfree(hif_drv->conn_info.req_ies);
+       hif_drv->conn_info.req_ies = NULL;
+       hif_drv->hif_state = HOST_IF_IDLE;
+}
+
+static void handle_rcvd_gnrl_async_info(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+       struct wilc_rcvd_mac_info *mac_info = &msg->body.mac_info;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+
+       if (!hif_drv) {
+               netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__);
+               goto free_msg;
+       }
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                  "Current State = %d,Received state = %d\n",
+                  hif_drv->hif_state, mac_info->status);
+
+       if (!hif_drv->conn_info.conn_result) {
+               PRINT_ER(vif->ndev, "conn_result is NULL\n");
+               goto free_msg;
+       }
+       if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
+               host_int_parse_assoc_resp_info(vif, mac_info->status);
+       } else if (mac_info->status == WILC_MAC_STATUS_DISCONNECTED) {
+               if (hif_drv->hif_state == HOST_IF_CONNECTED) {
+                       host_int_handle_disconnect(vif);
+               } else if (hif_drv->usr_scan_req.scan_result) {
+                       PRINT_WRN(vif->ndev, HOSTINF_DBG,
+                                 "Received WILC_MAC_STATUS_DISCONNECTED. Abort the running Scan");
+                       del_timer(&hif_drv->scan_timer);
+                       handle_scan_done(vif, SCAN_EVENT_ABORTED);
+               }
+       }
+
+free_msg:
+       kfree(msg);
+}
+
+int wilc_disconnect(struct wilc_vif *vif)
+{
+       struct wid wid;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct wilc_user_scan_req *scan_req;
+       struct wilc_conn_info *conn_info;
+       int result;
+       u16 dummy_reason_code = 0;
+       struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+                                                             WILC_P2P_IFC);
+       struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+                                                              WILC_WLAN_IFC);
+
+       if (hif_drv_wlan != NULL) {
+               if (hif_drv_wlan->hif_state == HOST_IF_SCANNING) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Abort Scan. WLAN_IFC is in state [%d]\n",
+                                  hif_drv_wlan->hif_state);
+                       del_timer(&hif_drv_wlan->scan_timer);
+                       handle_scan_done(vif, SCAN_EVENT_ABORTED);
+               }
+       }
+       if (hif_drv_p2p != NULL) {
+               if (hif_drv_p2p->hif_state == HOST_IF_SCANNING) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Abort Scan. P2P_IFC is in state [%d]\n",
+                                  hif_drv_p2p->hif_state);
+                       del_timer(&hif_drv_p2p->scan_timer);
+                       handle_scan_done(vif, SCAN_EVENT_ABORTED);
+               }
+       }
+       wid.id = WID_DISCONNECT;
+       wid.type = WID_CHAR;
+       wid.val = (s8 *)&dummy_reason_code;
+       wid.size = sizeof(char);
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending disconnect request\n");
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+
+       if (result) {
+               PRINT_ER(vif->ndev, "Failed to send disconnect\n");
+               return -ENOMEM;
+       }
+
+       scan_req = &hif_drv->usr_scan_req;
+       conn_info = &hif_drv->conn_info;
+
+       if (scan_req->scan_result) {
+               del_timer(&hif_drv->scan_timer);
+               scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->arg);
+               scan_req->scan_result = NULL;
+       }
+
+       if (conn_info->conn_result) {
+               if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
+                       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                                  "supplicant requested disconnection\n");
+                       del_timer(&hif_drv->connect_timer);
+                       conn_info->conn_result(EVENT_CONN_RESP,
+                                              WILC_MAC_STATUS_DISCONNECTED,
+                                              conn_info->arg);
+
+               } else if (hif_drv->hif_state == HOST_IF_CONNECTED) {
+                       conn_info->conn_result(EVENT_DISCONN_NOTIF,
+                                              WILC_MAC_STATUS_DISCONNECTED,
+                                              conn_info->arg);
+               }
+       } else {
+               PRINT_ER(vif->ndev, "conn_result = NULL\n");
+       }
+
+       hif_drv->hif_state = HOST_IF_IDLE;
+
+       eth_zero_addr(hif_drv->assoc_bssid);
+
+       conn_info->req_ies_len = 0;
+       kfree(conn_info->req_ies);
+       conn_info->req_ies = NULL;
+
+       return 0;
+}
+
+void wilc_resolve_disconnect_aberration(struct wilc_vif *vif)
+{
+       if (!vif->hif_drv)
+               return;
+       if (vif->hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
+           vif->hif_drv->hif_state == HOST_IF_CONNECTING) {
+               PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                          "\n\n<< correcting Supplicant state machine >>\n\n");
+               wilc_disconnect(vif);
+       }
+}
+
+int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats)
+{
+       struct wid wid_list[5];
+       u32 wid_cnt = 0, result;
+
+       wid_list[wid_cnt].id = WID_LINKSPEED;
+       wid_list[wid_cnt].type = WID_CHAR;
+       wid_list[wid_cnt].size = sizeof(char);
+       wid_list[wid_cnt].val = (s8 *)&stats->link_speed;
+       wid_cnt++;
+
+       wid_list[wid_cnt].id = WID_RSSI;
+       wid_list[wid_cnt].type = WID_CHAR;
+       wid_list[wid_cnt].size = sizeof(char);
+       wid_list[wid_cnt].val = (s8 *)&stats->rssi;
+       wid_cnt++;
+
+       wid_list[wid_cnt].id = WID_SUCCESS_FRAME_COUNT;
+       wid_list[wid_cnt].type = WID_INT;
+       wid_list[wid_cnt].size = sizeof(u32);
+       wid_list[wid_cnt].val = (s8 *)&stats->tx_cnt;
+       wid_cnt++;
+
+       wid_list[wid_cnt].id = WID_RECEIVED_FRAGMENT_COUNT;
+       wid_list[wid_cnt].type = WID_INT;
+       wid_list[wid_cnt].size = sizeof(u32);
+       wid_list[wid_cnt].val = (s8 *)&stats->rx_cnt;
+       wid_cnt++;
+
+       wid_list[wid_cnt].id = WID_FAILED_COUNT;
+       wid_list[wid_cnt].type = WID_INT;
+       wid_list[wid_cnt].size = sizeof(u32);
+       wid_list[wid_cnt].val = (s8 *)&stats->tx_fail_cnt;
+       wid_cnt++;
+
+       result = wilc_send_config_pkt(vif, WILC_GET_CFG, wid_list,
+                                     wid_cnt,
+                                     wilc_get_vif_idx(vif));
+
+       if (result) {
+               PRINT_ER(vif->ndev, "Failed to send scan parameters\n");
+               return result;
+       }
+
+       if (stats->link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
+           stats->link_speed != DEFAULT_LINK_SPEED) {
+               PRINT_INFO(vif->ndev, HOSTINF_DBG, "Enable TCP filter\n");
+               wilc_enable_tcp_ack_filter(vif, true);
+       } else if (stats->link_speed != DEFAULT_LINK_SPEED) {
+               PRINT_INFO(vif->ndev, HOSTINF_DBG, "Disable TCP filter %d\n",
+                          stats->link_speed);
+               wilc_enable_tcp_ack_filter(vif, false);
+       }
+
+       return result;
+}
+
+static void handle_get_statistics(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+       struct rf_info *stats = (struct rf_info *)msg->body.data;
+
+       wilc_get_statistics(vif, stats);
+       kfree(msg);
+}
+
+static void wilc_hif_pack_sta_param(struct wilc_vif *vif, u8 *cur_byte,
+                                   const u8 *mac,
+                                   struct station_parameters *params)
+{
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Packing STA params\n");
+       ether_addr_copy(cur_byte, mac);
+       cur_byte +=  ETH_ALEN;
+
+       put_unaligned_le16(params->aid, cur_byte);
+       cur_byte += 2;
+
+       *cur_byte++ = params->supported_rates_len;
+       if (params->supported_rates_len > 0)
+               memcpy(cur_byte, params->supported_rates,
+                      params->supported_rates_len);
+       cur_byte += params->supported_rates_len;
+
+       if (params->ht_capa) {
+               *cur_byte++ = true;
+               memcpy(cur_byte, &params->ht_capa,
+                      sizeof(struct ieee80211_ht_cap));
+       } else {
+               *cur_byte++ = false;
+       }
+       cur_byte += sizeof(struct ieee80211_ht_cap);
+
+       put_unaligned_le16(params->sta_flags_mask, cur_byte);
+       cur_byte += 2;
+       put_unaligned_le16(params->sta_flags_set, cur_byte);
+}
+
+static int handle_remain_on_chan(struct wilc_vif *vif,
+                                struct wilc_remain_ch *hif_remain_ch)
+{
+       int result;
+       u8 remain_on_chan_flag;
+       struct wid wid;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct host_if_drv *hif_drv_p2p = get_drv_hndl_by_ifc(vif->wilc,
+                                                             WILC_P2P_IFC);
+       struct host_if_drv *hif_drv_wlan = get_drv_hndl_by_ifc(vif->wilc,
+                                                              WILC_WLAN_IFC);
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "Driver is null\n");
+               return -EFAULT;
+       }
+
+       if (hif_drv_p2p != NULL) {
+               if (hif_drv_p2p->hif_state == HOST_IF_SCANNING) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "IFC busy scanning P2P_IFC state %d\n",
+                                  hif_drv_p2p->hif_state);
+                       return -EBUSY;
+               } else if ((hif_drv_p2p->hif_state != HOST_IF_IDLE) &&
+               (hif_drv_p2p->hif_state != HOST_IF_CONNECTED)) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "IFC busy connecting. P2P_IFC state %d\n",
+                                  hif_drv_p2p->hif_state);
+                       return -EBUSY;
+               }
+       }
+       if (hif_drv_wlan != NULL) {
+               if (hif_drv_wlan->hif_state == HOST_IF_SCANNING) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "IFC busy scanning. WLAN_IFC state %d\n",
+                                  hif_drv_wlan->hif_state);
+                       return -EBUSY;
+               } else if ((hif_drv_wlan->hif_state != HOST_IF_IDLE) &&
+               (hif_drv_wlan->hif_state != HOST_IF_CONNECTED)) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "IFC busy connecting. WLAN_IFC %d\n",
+                                  hif_drv_wlan->hif_state);
+                       return -EBUSY;
+               }
+       }
+
+       if (vif->connecting) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Don't do scan in (CONNECTING) state\n");
+               return -EBUSY;
+       }
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       if (vif->obtaining_ip) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Don't obss scan until IP adresss is obtained\n");
+               return -EBUSY;
+       }
+#endif
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting channel [%d] duration[%d] [%llu]\n",
+                  hif_remain_ch->ch, hif_remain_ch->duration,
+                  hif_remain_ch->cookie);
+       remain_on_chan_flag = true;
+       wid.id = WID_REMAIN_ON_CHAN;
+       wid.type = WID_STR;
+       wid.size = 2;
+       wid.val = kmalloc(wid.size, GFP_KERNEL);
+       if (!wid.val)
+               return -ENOMEM;
+
+       wid.val[0] = remain_on_chan_flag;
+       wid.val[1] = (s8)hif_remain_ch->ch;
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       kfree(wid.val);
+       if (result) {
+               PRINT_ER(vif->ndev, "Failed to set remain on channel\n");
+               return -EBUSY;
+       }
+
+       hif_drv->remain_on_ch.arg = hif_remain_ch->arg;
+       hif_drv->remain_on_ch.expired = hif_remain_ch->expired;
+       hif_drv->remain_on_ch.ch = hif_remain_ch->ch;
+       hif_drv->remain_on_ch.cookie = hif_remain_ch->cookie;
+       hif_drv->hif_state = HOST_IF_P2P_LISTEN;
+
+       hif_drv->remain_on_ch_timer_vif = vif;
+
+       return result;
+}
+
+static void handle_listen_state_expired(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+       struct wilc_remain_ch *hif_remain_ch = &msg->body.remain_on_ch;
+       u8 remain_on_chan_flag;
+       struct wid wid;
+       int result;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       u8 null_bssid[6] = {0};
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "CANCEL REMAIN ON CHAN\n");
+
+       if (hif_drv->hif_state == HOST_IF_P2P_LISTEN) {
+               remain_on_chan_flag = false;
+               wid.id = WID_REMAIN_ON_CHAN;
+               wid.type = WID_STR;
+               wid.size = 2;
+               wid.val = kmalloc(wid.size, GFP_KERNEL);
+
+               if (!wid.val) {
+                       PRINT_ER(vif->ndev, "Failed to allocate memory\n");
+                       goto free_msg;
+               }
+
+               wid.val[0] = remain_on_chan_flag;
+               wid.val[1] = WILC_FALSE_FRMWR_CHANNEL;
+
+               result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                             wilc_get_vif_idx(vif));
+               kfree(wid.val);
+               if (result != 0) {
+                       PRINT_ER(vif->ndev, "Failed to set remain channel\n");
+                       goto free_msg;
+               }
+
+               if (hif_drv->remain_on_ch.expired)
+                       hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.arg,
+                                                     hif_remain_ch->cookie);
+
+               if (memcmp(hif_drv->assoc_bssid, null_bssid, ETH_ALEN) == 0)
+                       hif_drv->hif_state = HOST_IF_IDLE;
+               else
+                       hif_drv->hif_state = HOST_IF_CONNECTED;
+       } else {
+               PRINT_D(vif->ndev, GENERIC_DBG,  "Not in listen state\n");
+       }
+
+free_msg:
+       kfree(msg);
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void listen_timer_cb(struct timer_list *t)
+#else
+static void listen_timer_cb(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       struct host_if_drv *hif_drv = from_timer(hif_drv, t,
+                                                     remain_on_ch_timer);
+#else
+       struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+#endif
+       struct wilc_vif *vif = hif_drv->remain_on_ch_timer_vif;
+       int result;
+       struct host_if_msg *msg;
+
+       del_timer(&vif->hif_drv->remain_on_ch_timer);
+
+       msg = wilc_alloc_work(vif, handle_listen_state_expired, false);
+       if (IS_ERR(msg))
+               return;
+
+       msg->body.remain_on_ch.cookie = vif->hif_drv->remain_on_ch.cookie;
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "wilc_mq_send fail\n");
+               kfree(msg);
+       }
+}
+
+static void handle_set_mcast_filter(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+       struct wilc_set_multicast *set_mc = &msg->body.mc_info;
+       int result;
+       struct wid wid;
+       u8 *cur_byte;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setup Multicast Filter\n");
+
+       wid.id = WID_SETUP_MULTICAST_FILTER;
+       wid.type = WID_BIN;
+       wid.size = sizeof(struct wilc_set_multicast) + (set_mc->cnt * ETH_ALEN);
+       wid.val = kmalloc(wid.size, GFP_KERNEL);
+       if (!wid.val)
+               goto error;
+
+       cur_byte = wid.val;
+       put_unaligned_le32(set_mc->enabled, cur_byte);
+       cur_byte += 4;
+
+       put_unaligned_le32(set_mc->cnt, cur_byte);
+       cur_byte += 4;
+
+       if (set_mc->cnt > 0 && set_mc->mc_list)
+               memcpy(cur_byte, set_mc->mc_list, set_mc->cnt * ETH_ALEN);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send setup multicast\n");
+
+error:
+       kfree(set_mc->mc_list);
+       kfree(wid.val);
+       kfree(msg);
+}
+
+void wilc_set_wowlan_trigger(struct wilc_vif *vif, u8 wowlan_trigger)
+{
+       int ret;
+       struct wid wid;
+
+       wid.id = WID_WOWLAN_TRIGGER;
+       wid.type = WID_CHAR;
+       wid.val = &wowlan_trigger;
+       wid.size = sizeof(s8);
+
+       ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                  wilc_get_vif_idx(vif));
+
+       if (ret)
+               PRINT_ER(vif->ndev,
+                        "Failed to send wowlan trigger config packet\n");
+}
+
+static void handle_scan_timer(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       int ret;
+
+       PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "handling scan timer\n");
+       ret = handle_scan_done(msg->vif, SCAN_EVENT_ABORTED);
+       if (ret)
+               PRINT_ER(msg->vif->ndev, "Failed to handle scan done\n");
+       kfree(msg);
+}
+
+static void handle_scan_complete(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+
+       del_timer(&msg->vif->hif_drv->scan_timer);
+       PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "scan completed\n");
+
+       handle_scan_done(msg->vif, SCAN_EVENT_DONE);
+
+       kfree(msg);
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void timer_scan_cb(struct timer_list *t)
+#else
+static void timer_scan_cb(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       struct host_if_drv *hif_drv = from_timer(hif_drv, t, scan_timer);
+#else
+       struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+#endif
+       struct wilc_vif *vif = hif_drv->scan_timer_vif;
+       struct host_if_msg *msg;
+       int result;
+
+       msg = wilc_alloc_work(vif, handle_scan_timer, false);
+       if (IS_ERR(msg))
+               return;
+
+       result = wilc_enqueue_work(msg);
+       if (result)
+               kfree(msg);
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void timer_connect_cb(struct timer_list *t)
+#else
+static void timer_connect_cb(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       struct host_if_drv *hif_drv = from_timer(hif_drv, t, connect_timer);
+#else
+       struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+#endif
+       struct wilc_vif *vif = hif_drv->connect_timer_vif;
+       struct host_if_msg *msg;
+       int result;
+
+       msg = wilc_alloc_work(vif, handle_connect_timeout, false);
+       if (IS_ERR(msg))
+               return;
+
+       result = wilc_enqueue_work(msg);
+       if (result)
+               kfree(msg);
+}
+
+signed int wilc_send_buffered_eap(struct wilc_vif *vif,
+                                 void (*deliver_to_stack)(struct wilc_vif *,
+                                                          u8 *, u32, u32, u8),
+                                 void (*eap_buf_param)(void *), u8 *buff,
+                                 unsigned int size, unsigned int pkt_offset,
+                                 void *user_arg)
+{
+       int result;
+       struct host_if_msg *msg;
+
+       if (!vif || !deliver_to_stack || !eap_buf_param)
+               return -EFAULT;
+
+       msg = wilc_alloc_work(vif, handle_send_buffered_eap, false);
+       if (IS_ERR(msg))
+               return PTR_ERR(msg);
+       msg->body.send_buff_eap.deliver_to_stack = deliver_to_stack;
+       msg->body.send_buff_eap.eap_buf_param = eap_buf_param;
+       msg->body.send_buff_eap.size = size;
+       msg->body.send_buff_eap.pkt_offset = pkt_offset;
+       msg->body.send_buff_eap.buff = kmalloc(size + pkt_offset,
+                                                 GFP_ATOMIC);
+       memcpy(msg->body.send_buff_eap.buff, buff, size + pkt_offset);
+       msg->body.send_buff_eap.user_arg = user_arg;
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "enqueue work failed\n");
+               kfree(msg->body.send_buff_eap.buff);
+               kfree(msg);
+       }
+       return result;
+}
+
+int wilc_remove_wep_key(struct wilc_vif *vif, u8 index)
+{
+       struct wid wid;
+       int result;
+
+       wid.id = WID_REMOVE_WEP_KEY;
+       wid.type = WID_STR;
+       wid.size = sizeof(char);
+       wid.val = &index;
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev,
+                        "Failed to send remove wep key config packet\n");
+       return result;
+}
+
+int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index)
+{
+       struct wid wid;
+       int result;
+
+       wid.id = WID_KEY_ID;
+       wid.type = WID_CHAR;
+       wid.size = sizeof(char);
+       wid.val = &index;
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev,
+                        "Failed to send wep default key config packet\n");
+
+       return result;
+}
+
+int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
+                            u8 index)
+{
+       struct wid wid;
+       int result;
+       struct wilc_wep_key *wep_key;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling WEP key\n");
+       wid.id = WID_ADD_WEP_KEY;
+       wid.type = WID_STR;
+       wid.size = sizeof(*wep_key) + len;
+       wep_key = kzalloc(wid.size, GFP_KERNEL);
+       if (!wep_key) {
+               PRINT_ER(vif->ndev, "No buffer to send Key\n");
+               return -ENOMEM;
+       }
+       wid.val = (u8 *)wep_key;
+
+       wep_key->index = index;
+       wep_key->key_len = len;
+       memcpy(wep_key->key, key, len);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               netdev_err(vif->ndev,
+                          "Failed to add wep key config packet\n");
+
+       kfree(wep_key);
+       return result;
+}
+
+int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
+                           u8 index, u8 mode, enum authtype auth_type)
+{
+       struct wid wid_list[3];
+       int result;
+       struct wilc_wep_key *wep_key;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling WEP key index: %d\n",
+                  index);
+       wid_list[0].id = WID_11I_MODE;
+       wid_list[0].type = WID_CHAR;
+       wid_list[0].size = sizeof(char);
+       wid_list[0].val = &mode;
+
+       wid_list[1].id = WID_AUTH_TYPE;
+       wid_list[1].type = WID_CHAR;
+       wid_list[1].size = sizeof(char);
+       wid_list[1].val = (s8 *)&auth_type;
+
+       wid_list[2].id = WID_WEP_KEY_VALUE;
+       wid_list[2].type = WID_STR;
+       wid_list[2].size = sizeof(*wep_key) + len;
+       wep_key = kzalloc(wid_list[2].size, GFP_KERNEL);
+       if (!wep_key) {
+               PRINT_ER(vif->ndev, "No buffer to send Key\n");
+               return -ENOMEM;
+       }
+
+       wid_list[2].val = (u8 *)wep_key;
+
+       wep_key->index = index;
+       wep_key->key_len = len;
+       memcpy(wep_key->key, key, len);
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+                                     ARRAY_SIZE(wid_list),
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev,
+                        "Failed to add wep ap key config packet\n");
+
+       kfree(wep_key);
+       return result;
+}
+
+int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
+                const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic,
+                u8 mode, u8 cipher_mode, u8 index)
+{
+       int result = 0;
+       u8 t_key_len = ptk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN;
+
+       if (mode == WILC_AP_MODE) {
+               struct wid wid_list[2];
+               struct wilc_ap_wpa_ptk *key_buf;
+
+               wid_list[0].id = WID_11I_MODE;
+               wid_list[0].type = WID_CHAR;
+               wid_list[0].size = sizeof(char);
+               wid_list[0].val = (s8 *)&cipher_mode;
+
+               key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL);
+               if (!key_buf) {
+                       PRINT_ER(vif->ndev,
+                                "NO buffer to keep Key buffer - AP\n");
+                       return -ENOMEM;
+               }
+               ether_addr_copy(key_buf->mac_addr, mac_addr);
+               key_buf->index = index;
+               key_buf->key_len = t_key_len;
+               memcpy(&key_buf->key[0], ptk, ptk_key_len);
+
+               if (rx_mic)
+                       memcpy(&key_buf->key[ptk_key_len], rx_mic,
+                              WILC_RX_MIC_KEY_LEN);
+
+               if (tx_mic)
+                       memcpy(&key_buf->key[ptk_key_len + WILC_RX_MIC_KEY_LEN],
+                              tx_mic, WILC_TX_MIC_KEY_LEN);
+
+               wid_list[1].id = WID_ADD_PTK;
+               wid_list[1].type = WID_STR;
+               wid_list[1].size = sizeof(*key_buf) + t_key_len;
+               wid_list[1].val = (u8 *)key_buf;
+               result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+                                             ARRAY_SIZE(wid_list),
+                                             wilc_get_vif_idx(vif));
+               kfree(key_buf);
+       } else if (mode == WILC_STATION_MODE) {
+               struct wid wid;
+               struct wilc_sta_wpa_ptk *key_buf;
+
+               key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL);
+               if (!key_buf) {
+                       PRINT_ER(vif->ndev,
+                                "No buffer to keep Key buffer - Station\n");
+                       return -ENOMEM;
+               }
+
+               ether_addr_copy(key_buf->mac_addr, mac_addr);
+               key_buf->key_len = t_key_len;
+               memcpy(&key_buf->key[0], ptk, ptk_key_len);
+
+               if (rx_mic)
+                       memcpy(&key_buf->key[ptk_key_len], rx_mic,
+                              WILC_RX_MIC_KEY_LEN);
+
+               if (tx_mic)
+                       memcpy(&key_buf->key[ptk_key_len + WILC_RX_MIC_KEY_LEN],
+                              tx_mic, WILC_TX_MIC_KEY_LEN);
+
+               wid.id = WID_ADD_PTK;
+               wid.type = WID_STR;
+               wid.size = sizeof(*key_buf) + t_key_len;
+               wid.val = (s8 *)key_buf;
+               result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                             wilc_get_vif_idx(vif));
+               kfree(key_buf);
+       }
+
+       return result;
+}
+
+int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
+                   u8 index, u32 key_rsc_len, const u8 *key_rsc,
+                   const u8 *rx_mic, const u8 *tx_mic, u8 mode,
+                   u8 cipher_mode)
+{
+       int result = 0;
+       struct wilc_gtk_key *gtk_key;
+       int t_key_len = gtk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN;
+
+       gtk_key = kzalloc(sizeof(*gtk_key) + t_key_len, GFP_KERNEL);
+       if (!gtk_key) {
+               PRINT_ER(vif->ndev, "No buffer to send GTK Key\n");
+               return -ENOMEM;
+       }
+
+       /* fill bssid value only in station mode */
+       if (mode == WILC_STATION_MODE &&
+           vif->hif_drv->hif_state == HOST_IF_CONNECTED)
+               memcpy(gtk_key->mac_addr, vif->hif_drv->assoc_bssid, ETH_ALEN);
+
+       if (key_rsc)
+               memcpy(gtk_key->rsc, key_rsc, 8);
+       gtk_key->index = index;
+       gtk_key->key_len = t_key_len;
+       memcpy(&gtk_key->key[0], rx_gtk, gtk_key_len);
+
+       if (rx_mic)
+               memcpy(&gtk_key->key[gtk_key_len], rx_mic, WILC_RX_MIC_KEY_LEN);
+
+       if (tx_mic)
+               memcpy(&gtk_key->key[gtk_key_len + WILC_RX_MIC_KEY_LEN],
+                      tx_mic, WILC_TX_MIC_KEY_LEN);
+
+       if (mode == WILC_AP_MODE) {
+               struct wid wid_list[2];
+
+               wid_list[0].id = WID_11I_MODE;
+               wid_list[0].type = WID_CHAR;
+               wid_list[0].size = sizeof(char);
+               wid_list[0].val = (s8 *)&cipher_mode;
+
+               wid_list[1].id = WID_ADD_RX_GTK;
+               wid_list[1].type = WID_STR;
+               wid_list[1].size = sizeof(*gtk_key) + t_key_len;
+               wid_list[1].val = (u8 *)gtk_key;
+
+               result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+                                             ARRAY_SIZE(wid_list),
+                                             wilc_get_vif_idx(vif));
+               kfree(gtk_key);
+       } else if (mode == WILC_STATION_MODE) {
+               struct wid wid;
+
+               wid.id = WID_ADD_RX_GTK;
+               wid.type = WID_STR;
+               wid.size = sizeof(*gtk_key) + t_key_len;
+               wid.val = (u8 *)gtk_key;
+               result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                             wilc_get_vif_idx(vif));
+               kfree(gtk_key);
+       }
+
+       return result;
+}
+
+int wilc_set_pmkid_info(struct wilc_vif *vif, struct wilc_pmkid_attr *pmkid)
+{
+       struct wid wid;
+
+       wid.id = WID_PMKID_INFO;
+       wid.type = WID_STR;
+       wid.size = (pmkid->numpmkid * sizeof(struct wilc_pmkid)) + 1;
+       wid.val = (u8 *)pmkid;
+
+       return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+}
+
+int wilc_get_mac_address(struct wilc_vif *vif, u8 *mac_addr)
+{
+       int result;
+       struct wid wid;
+
+       wid.id = WID_MAC_ADDR;
+       wid.type = WID_STR;
+       wid.size = ETH_ALEN;
+       wid.val = mac_addr;
+
+       result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               netdev_err(vif->ndev, "Failed to get mac address\n");
+
+       return result;
+}
+
+int wilc_set_mac_address(struct wilc_vif *vif, u8 *mac_addr)
+{
+       struct wid wid;
+       int result;
+
+       wid.id = WID_MAC_ADDR;
+       wid.type = WID_STR;
+       wid.size = ETH_ALEN;
+       wid.val = mac_addr;
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to set mac address\n");
+
+       return result;
+}
+
+int wilc_set_join_req(struct wilc_vif *vif, u8 *bssid, const u8 *ies,
+                     size_t ies_len)
+{
+       int result;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       struct wilc_conn_info *conn_info = &hif_drv->conn_info;
+
+       if (bssid)
+               ether_addr_copy(conn_info->bssid, bssid);
+
+       if (ies) {
+               conn_info->req_ies_len = ies_len;
+               conn_info->req_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+               if (!conn_info->req_ies)
+                       return -ENOMEM;
+       }
+
+       result = wilc_send_connect_wid(vif);
+       if (result) {
+               PRINT_ER(vif->ndev, "Failed to send connect wid\n");
+               goto free_ies;
+       }
+
+#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+       hif_drv->connect_timer.data = (unsigned long)hif_drv;
+#endif
+       hif_drv->connect_timer_vif = vif;
+       mod_timer(&hif_drv->connect_timer,
+                 jiffies + msecs_to_jiffies(WILC_HIF_CONNECT_TIMEOUT_MS));
+
+       return 0;
+
+free_ies:
+       kfree(conn_info->req_ies);
+
+       return result;
+}
+
+int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel)
+{
+       struct wid wid;
+       int result;
+
+       wid.id = WID_CURRENT_CHANNEL;
+       wid.type = WID_CHAR;
+       wid.size = sizeof(char);
+       wid.val = &channel;
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to set channel\n");
+
+       return result;
+}
+
+int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
+                            u8 ifc_id)
+{
+       struct wid wid;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+       int result;
+       struct wilc_drv_handler drv;
+
+       if (!hif_drv)
+               return -EFAULT;
+
+       wid.id = WID_SET_DRV_HANDLER;
+       wid.type = WID_STR;
+       wid.size = sizeof(drv);
+       wid.val = (u8 *)&drv;
+
+       drv.handler = cpu_to_le32(index);
+       drv.mode = (ifc_id | (mode << 1));
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     hif_drv->driver_handler_id);
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to set driver handler\n");
+
+       return result;
+}
+
+int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode)
+{
+       struct wid wid;
+       struct wilc_op_mode op_mode;
+       int result;
+
+       wid.id = WID_SET_OPERATION_MODE;
+       wid.type = WID_INT;
+       wid.size = sizeof(op_mode);
+       wid.val = (u8 *)&op_mode;
+
+       op_mode.mode = cpu_to_le32(mode);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to set operation mode\n");
+
+       return result;
+}
+
+s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac, u32 *out_val)
+{
+       struct wid wid;
+       s32 result;
+
+       wid.id = WID_SET_STA_MAC_INACTIVE_TIME;
+       wid.type = WID_STR;
+       wid.size = ETH_ALEN;
+       wid.val = kzalloc(wid.size, GFP_KERNEL);
+       if (!wid.val) {
+               PRINT_ER(vif->ndev, "Failed to allocate buffer\n");
+               return -ENOMEM;
+       }
+
+       ether_addr_copy(wid.val, mac);
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       kfree(wid.val);
+       if (result) {
+               PRINT_ER(vif->ndev, "Failed to set inactive mac\n");
+               return result;
+       }
+
+       wid.id = WID_GET_INACTIVE_TIME;
+       wid.type = WID_INT;
+       wid.val = (s8 *)out_val;
+       wid.size = sizeof(u32);
+       result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to get inactive time\n");
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Getting inactive time : %d\n",
+                  *out_val);
+
+       return result;
+}
+
+int wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level)
+{
+       struct wid wid;
+       int result;
+
+       if (!rssi_level) {
+               PRINT_ER(vif->ndev, "RSS pointer value is null\n");
+               return -EFAULT;
+       }
+
+       wid.id = WID_RSSI;
+       wid.type = WID_CHAR;
+       wid.size = sizeof(char);
+       wid.val = rssi_level;
+       result = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               netdev_err(vif->ndev, "Failed to get RSSI value\n");
+
+       return result;
+}
+
+int wilc_get_stats_async(struct wilc_vif *vif, struct rf_info *stats)
+{
+       int result;
+       struct host_if_msg *msg;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, " getting async statistics\n");
+       msg = wilc_alloc_work(vif, handle_get_statistics, false);
+       if (IS_ERR(msg))
+               return PTR_ERR(msg);
+
+       msg->body.data = (char *)stats;
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "enqueue work failed\n");
+               kfree(msg);
+               return result;
+       }
+
+       return result;
+}
+
+int wilc_hif_set_cfg(struct wilc_vif *vif, struct cfg_param_attr *param)
+{
+       struct wid wid_list[4];
+       int i = 0;
+
+       if (param->flag & WILC_CFG_PARAM_RETRY_SHORT) {
+               wid_list[i].id = WID_SHORT_RETRY_LIMIT;
+               wid_list[i].val = (s8 *)&param->short_retry_limit;
+               wid_list[i].type = WID_SHORT;
+               wid_list[i].size = sizeof(u16);
+               i++;
+       }
+       if (param->flag & WILC_CFG_PARAM_RETRY_LONG) {
+               wid_list[i].id = WID_LONG_RETRY_LIMIT;
+               wid_list[i].val = (s8 *)&param->long_retry_limit;
+               wid_list[i].type = WID_SHORT;
+               wid_list[i].size = sizeof(u16);
+               i++;
+       }
+       if (param->flag & WILC_CFG_PARAM_FRAG_THRESHOLD) {
+               wid_list[i].id = WID_FRAG_THRESHOLD;
+               wid_list[i].val = (s8 *)&param->frag_threshold;
+               wid_list[i].type = WID_SHORT;
+               wid_list[i].size = sizeof(u16);
+               i++;
+       }
+       if (param->flag & WILC_CFG_PARAM_RTS_THRESHOLD) {
+               wid_list[i].id = WID_RTS_THRESHOLD;
+               wid_list[i].val = (s8 *)&param->rts_threshold;
+               wid_list[i].type = WID_SHORT;
+               wid_list[i].size = sizeof(u16);
+               i++;
+       }
+
+       return wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list,
+                                     i, wilc_get_vif_idx(vif));
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+static void get_periodic_rssi(struct timer_list *t)
+#else
+static void get_periodic_rssi(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       struct wilc_vif *vif = from_timer(vif, t, periodic_rssi);
+#else
+       struct wilc_vif *vif = (struct wilc_vif *)arg;
+#endif
+
+       if (!vif->hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               return;
+       }
+
+       if (vif->hif_drv->hif_state == HOST_IF_CONNECTED)
+               wilc_get_stats_async(vif, &vif->periodic_stats);
+
+       mod_timer(&vif->periodic_rssi, jiffies + msecs_to_jiffies(5000));
+}
+
+int wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
+{
+       struct host_if_drv *hif_drv;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       int i;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Initializing host interface for client %d\n",
+                  wilc->clients_count + 1);
+
+       hif_drv  = kzalloc(sizeof(*hif_drv), GFP_KERNEL);
+       if (!hif_drv) {
+               PRINT_ER(dev, "hif driver is NULL\n");
+               return -ENOMEM;
+       }
+       *hif_drv_handler = hif_drv;
+       for (i = 0; i < wilc->vif_num; i++)
+               if (dev == wilc->vif[i]->ndev) {
+                       wilc->vif[i]->hif_drv = hif_drv;
+                       hif_drv->driver_handler_id = i + 1;
+                       break;
+               }
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       vif->obtaining_ip = false;
+#endif
+
+       if (wilc->clients_count == 0)
+               mutex_init(&wilc->deinit_lock);
+
+       #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+               timer_setup(&vif->periodic_rssi, get_periodic_rssi, 0);
+       #else
+               setup_timer(&vif->periodic_rssi, get_periodic_rssi,
+                           (unsigned long)vif);
+       #endif
+               mod_timer(&vif->periodic_rssi,
+                         jiffies + msecs_to_jiffies(5000));
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       timer_setup(&hif_drv->scan_timer, timer_scan_cb, 0);
+       timer_setup(&hif_drv->connect_timer, timer_connect_cb, 0);
+       timer_setup(&hif_drv->remain_on_ch_timer, listen_timer_cb, 0);
+#else
+       setup_timer(&hif_drv->scan_timer, timer_scan_cb, 0);
+       setup_timer(&hif_drv->connect_timer, timer_connect_cb, 0);
+       setup_timer(&hif_drv->remain_on_ch_timer, listen_timer_cb, 0);
+#endif
+
+       hif_drv->hif_state = HOST_IF_IDLE;
+
+       hif_drv->p2p_timeout = 0;
+
+       wilc->clients_count++;
+
+       return 0;
+}
+
+int wilc_deinit(struct wilc_vif *vif)
+{
+       int result = 0;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               return -EFAULT;
+       }
+
+       mutex_lock(&vif->wilc->deinit_lock);
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "De-initializing host interface for client %d\n",
+                  vif->wilc->clients_count);
+
+       del_timer_sync(&hif_drv->scan_timer);
+       del_timer_sync(&hif_drv->connect_timer);
+       del_timer_sync(&vif->periodic_rssi);
+       del_timer_sync(&hif_drv->remain_on_ch_timer);
+
+       if (hif_drv->usr_scan_req.scan_result) {
+               hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED, NULL,
+                                                 hif_drv->usr_scan_req.arg);
+               hif_drv->usr_scan_req.scan_result = NULL;
+       }
+
+       hif_drv->hif_state = HOST_IF_IDLE;
+
+       kfree(hif_drv);
+       vif->hif_drv = NULL;
+
+       vif->wilc->clients_count--;
+       mutex_unlock(&vif->wilc->deinit_lock);
+       return result;
+}
+
+void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+       int result;
+       struct host_if_msg *msg;
+       int id;
+       struct host_if_drv *hif_drv;
+       struct wilc_vif *vif;
+
+       id = get_unaligned_le32(&buffer[length - 4]);
+       vif = wilc_get_vif_from_idx(wilc, id);
+       if (!vif)
+               return;
+       hif_drv = vif->hif_drv;
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "driver not init[%p]\n", hif_drv);
+               return;
+       }
+
+       msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false);
+       if (IS_ERR(msg))
+               return;
+
+       msg->body.net_info.frame_len = get_unaligned_le16(&buffer[6]) - 1;
+       msg->body.net_info.rssi = buffer[8];
+       msg->body.net_info.mgmt = kmemdup(&buffer[9],
+                                         msg->body.net_info.frame_len,
+                                         GFP_KERNEL);
+       if (!msg->body.net_info.mgmt) {
+               kfree(msg);
+               return;
+       }
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "message parameters (%d)\n", result);
+               kfree(msg->body.net_info.mgmt);
+               kfree(msg);
+       }
+}
+
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+       int result;
+       struct host_if_msg *msg;
+       int id;
+       struct host_if_drv *hif_drv;
+       struct wilc_vif *vif;
+
+       mutex_lock(&wilc->deinit_lock);
+
+       id = get_unaligned_le32(&buffer[length - 4]);
+       vif = wilc_get_vif_from_idx(wilc, id);
+       if (!vif) {
+               mutex_unlock(&wilc->deinit_lock);
+               return;
+       }
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "General asynchronous info packet received\n");
+
+       hif_drv = vif->hif_drv;
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               mutex_unlock(&wilc->deinit_lock);
+               return;
+       }
+
+       if (!hif_drv->conn_info.conn_result) {
+               PRINT_ER(vif->ndev, "there is no current Connect Request\n");
+               mutex_unlock(&wilc->deinit_lock);
+               return;
+       }
+
+       msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false);
+       if (IS_ERR(msg)) {
+               mutex_unlock(&wilc->deinit_lock);
+               return;
+       }
+
+       msg->body.mac_info.status = buffer[7];
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Received MAC status= %d Reason= %d Info = %d\n",
+                  buffer[7], buffer[8], buffer[9]);
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "enqueue work failed\n");
+               kfree(msg);
+       }
+
+       mutex_unlock(&wilc->deinit_lock);
+}
+
+void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+       int result;
+       int id;
+       struct host_if_drv *hif_drv;
+       struct wilc_vif *vif;
+
+       id = get_unaligned_le32(&buffer[length - 4]);
+       vif = wilc_get_vif_from_idx(wilc, id);
+       if (!vif)
+               return;
+       hif_drv = vif->hif_drv;
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "Scan notification received\n");
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               return;
+       }
+
+       if (hif_drv->usr_scan_req.scan_result) {
+               struct host_if_msg *msg;
+
+               msg = wilc_alloc_work(vif, handle_scan_complete, false);
+               if (IS_ERR(msg))
+                       return;
+
+               result = wilc_enqueue_work(msg);
+               if (result) {
+                       PRINT_ER(vif->ndev, "enqueue work failed\n");
+                       kfree(msg);
+               }
+       }
+}
+
+int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie,
+                          u32 duration, u16 chan,
+                          void (*expired)(void *, u64), void *user_arg)
+{
+       struct wilc_remain_ch roc;
+       int result;
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "%s called\n", __func__);
+       roc.ch = chan;
+       roc.expired = expired;
+       roc.arg = user_arg;
+       roc.duration = duration;
+       roc.cookie = cookie;
+       result = handle_remain_on_chan(vif, &roc);
+       if (result)
+               PRINT_ER(vif->ndev, "%s: failed to set remain on channel\n",
+                        __func__);
+
+       return result;
+}
+
+int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie)
+{
+       int result;
+       struct host_if_msg *msg;
+       struct host_if_drv *hif_drv = vif->hif_drv;
+
+       if (!hif_drv) {
+               PRINT_ER(vif->ndev, "hif driver is NULL\n");
+               return -EFAULT;
+       }
+
+       del_timer(&hif_drv->remain_on_ch_timer);
+
+       msg = wilc_alloc_work(vif, handle_listen_state_expired, false);
+       if (IS_ERR(msg))
+               return PTR_ERR(msg);
+
+       msg->body.remain_on_ch.cookie = cookie;
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "enqueue work failed\n");
+               kfree(msg);
+       }
+
+       return result;
+}
+
+void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg)
+{
+       struct wid wid;
+       int result;
+       struct wilc_reg_frame reg_frame;
+
+       wid.id = WID_REGISTER_FRAME;
+       wid.type = WID_STR;
+       wid.size = sizeof(reg_frame);
+       wid.val = (u8 *)&reg_frame;
+
+       memset(&reg_frame, 0x0, sizeof(reg_frame));
+       reg_frame.reg = reg;
+
+       switch (frame_type) {
+       case IEEE80211_STYPE_ACTION:
+               PRINT_INFO(vif->ndev, HOSTINF_DBG, "ACTION\n");
+               reg_frame.reg_id = WILC_FW_ACTION_FRM_IDX;
+               break;
+
+       case IEEE80211_STYPE_PROBE_REQ:
+               PRINT_INFO(vif->ndev, HOSTINF_DBG, "PROBE REQ\n");
+               reg_frame.reg_id = WILC_FW_PROBE_REQ_IDX;
+               break;
+
+       default:
+               PRINT_INFO(vif->ndev, HOSTINF_DBG, "Not valid frame type\n");
+               break;
+       }
+       reg_frame.frame_type = cpu_to_le16(frame_type);
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to frame register\n");
+}
+
+int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
+                   struct cfg80211_beacon_data *params)
+{
+       struct wid wid;
+       int result;
+       u8 *cur_byte;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting adding beacon\n");
+
+       wid.id = WID_ADD_BEACON;
+       wid.type = WID_BIN;
+       wid.size = params->head_len + params->tail_len + 16;
+       wid.val = kzalloc(wid.size, GFP_KERNEL);
+       if (!wid.val) {
+               PRINT_ER(vif->ndev, "Failed to allocate buffer\n");
+               return -ENOMEM;
+       }
+
+       cur_byte = wid.val;
+       put_unaligned_le32(interval, cur_byte);
+       cur_byte += 4;
+       put_unaligned_le32(dtim_period, cur_byte);
+       cur_byte += 4;
+       put_unaligned_le32(params->head_len, cur_byte);
+       cur_byte += 4;
+
+       if (params->head_len > 0)
+               memcpy(cur_byte, params->head, params->head_len);
+       cur_byte += params->head_len;
+
+       put_unaligned_le32(params->tail_len, cur_byte);
+       cur_byte += 4;
+
+       if (params->tail_len > 0)
+               memcpy(cur_byte, params->tail, params->tail_len);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send add beacon\n");
+
+       kfree(wid.val);
+
+       return result;
+}
+
+int wilc_del_beacon(struct wilc_vif *vif)
+{
+       int result;
+       struct wid wid;
+       u8 del_beacon = 0;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting deleting beacon message queue params\n");
+
+       wid.id = WID_DEL_BEACON;
+       wid.type = WID_CHAR;
+       wid.size = sizeof(char);
+       wid.val = &del_beacon;
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send delete beacon\n");
+
+       return result;
+}
+
+int wilc_add_station(struct wilc_vif *vif, const u8 *mac,
+                    struct station_parameters *params)
+{
+       struct wid wid;
+       int result;
+       u8 *cur_byte;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting adding station message queue params\n");
+
+       wid.id = WID_ADD_STA;
+       wid.type = WID_BIN;
+       wid.size = WILC_ADD_STA_LENGTH + params->supported_rates_len;
+       wid.val = kmalloc(wid.size, GFP_KERNEL);
+       if (!wid.val)
+               return -ENOMEM;
+
+       cur_byte = wid.val;
+       wilc_hif_pack_sta_param(vif, cur_byte, mac, params);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result != 0)
+               PRINT_ER(vif->ndev, "Failed to send add station\n");
+
+       kfree(wid.val);
+
+       return result;
+}
+
+int wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr)
+{
+       struct wid wid;
+       int result;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting deleting station message queue params\n");
+
+       wid.id = WID_REMOVE_STA;
+       wid.type = WID_BIN;
+       wid.size = ETH_ALEN;
+       wid.val = kzalloc(wid.size, GFP_KERNEL);
+       if (!wid.val) {
+               PRINT_ER(vif->ndev, "Failed to allocate buffer\n");
+               return -ENOMEM;
+       }
+
+       if (!mac_addr)
+               eth_broadcast_addr(wid.val);
+       else
+               ether_addr_copy(wid.val, mac_addr);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to del station\n");
+
+       kfree(wid.val);
+
+       return result;
+}
+
+int wilc_del_allstation(struct wilc_vif *vif, u8 mac_addr[][ETH_ALEN])
+{
+       struct wid wid;
+       int result;
+       int i;
+       u8 assoc_sta = 0;
+       struct wilc_del_all_sta del_sta;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting deauthenticating station message queue params\n");
+       memset(&del_sta, 0x0, sizeof(del_sta));
+       for (i = 0; i < WILC_MAX_NUM_STA; i++) {
+               if (!is_zero_ether_addr(mac_addr[i])) {
+                       PRINT_INFO(vif->ndev,
+                                  CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n",
+                                  mac_addr[i][0], mac_addr[i][1],
+                                  mac_addr[i][2], mac_addr[i][3],
+                                  mac_addr[i][4], mac_addr[i][5]);
+                       assoc_sta++;
+                       ether_addr_copy(del_sta.mac[i], mac_addr[i]);
+               }
+       }
+       if (!assoc_sta) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "NO ASSOCIATED STAS\n");
+               return 0;
+       }
+       del_sta.assoc_sta = assoc_sta;
+
+       wid.id = WID_DEL_ALL_STA;
+       wid.type = WID_STR;
+       wid.size = (assoc_sta * ETH_ALEN) + 1;
+       wid.val = (u8 *)&del_sta;
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send delete all station\n");
+
+       return result;
+}
+
+int wilc_edit_station(struct wilc_vif *vif, const u8 *mac,
+                     struct station_parameters *params)
+{
+       struct wid wid;
+       int result;
+       u8 *cur_byte;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting editing station message queue params\n");
+
+       wid.id = WID_EDIT_STA;
+       wid.type = WID_BIN;
+       wid.size = WILC_ADD_STA_LENGTH + params->supported_rates_len;
+       wid.val = kmalloc(wid.size, GFP_KERNEL);
+       if (!wid.val)
+               return -ENOMEM;
+
+       cur_byte = wid.val;
+       wilc_hif_pack_sta_param(vif, cur_byte, mac, params);
+
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send edit station\n");
+
+       kfree(wid.val);
+       return result;
+}
+
+int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout)
+{
+       struct wid wid;
+       int result;
+       s8 power_mode;
+
+       if (wilc_wlan_get_num_conn_ifcs(vif->wilc) == 2 && enabled)
+               return 0;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG, "\n\n>> Setting PS to %d <<\n\n",
+                  enabled);
+       if (enabled)
+               power_mode = WILC_FW_MIN_FAST_PS;
+       else
+               power_mode = WILC_FW_NO_POWERSAVE;
+
+       wid.id = WID_POWER_MANAGEMENT;
+       wid.val = &power_mode;
+       wid.size = sizeof(char);
+       result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                     wilc_get_vif_idx(vif));
+       if (result)
+               PRINT_ER(vif->ndev, "Failed to send power management\n");
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       else
+               store_power_save_current_state(vif, power_mode);
+#endif
+
+       return result;
+}
+
+int wilc_setup_multicast_filter(struct wilc_vif *vif, u32 enabled, u32 count,
+                               u8 *mc_list)
+{
+       int result;
+       struct host_if_msg *msg;
+
+       PRINT_INFO(vif->ndev, HOSTINF_DBG,
+                  "Setting Multicast Filter params\n");
+       msg = wilc_alloc_work(vif, handle_set_mcast_filter, false);
+       if (IS_ERR(msg))
+               return PTR_ERR(msg);
+
+       msg->body.mc_info.enabled = enabled;
+       msg->body.mc_info.cnt = count;
+       msg->body.mc_info.mc_list = mc_list;
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "enqueue work failed\n");
+               kfree(msg);
+       }
+       return result;
+}
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+void handle_powersave_state_changes(struct work_struct *work)
+{
+       struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+       struct wilc_vif *vif = msg->vif;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "Recover PS = %d\n",
+                  vif->pwrsave_current_state);
+
+       /* Recover PS previous state */
+       wilc_set_power_mgmt(vif, vif->pwrsave_current_state, 0);
+}
+
+void wilc_powersave_state_changes(struct wilc_vif *vif)
+{
+       int result;
+       struct host_if_msg *msg;
+
+       msg = wilc_alloc_work(vif, handle_powersave_state_changes, false);
+       if (IS_ERR(msg))
+               return;
+
+       result = wilc_enqueue_work(msg);
+       if (result) {
+               PRINT_ER(vif->ndev, "enqueue work failed\n");
+               kfree(msg);
+       }
+}
+#endif
+
+int wilc_set_tx_power(struct wilc_vif *vif, u8 tx_power)
+{
+       struct wid wid;
+
+       wid.id = WID_TX_POWER;
+       wid.type = WID_CHAR;
+       wid.val = &tx_power;
+       wid.size = sizeof(char);
+
+       return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                  wilc_get_vif_idx(vif));
+}
+
+int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power)
+{
+       struct wid wid;
+
+       wid.id = WID_TX_POWER;
+       wid.type = WID_CHAR;
+       wid.val = tx_power;
+       wid.size = sizeof(char);
+
+       return wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1,
+                                   wilc_get_vif_idx(vif));
+}
+
+bool is_valid_gpio(struct wilc_vif *vif, u8 gpio)
+{
+       switch (vif->wilc->chip) {
+       case WILC_1000:
+               if (gpio == 0 || gpio == 1 || gpio == 4 || gpio == 6)
+                       return true;
+               else
+                       return false;
+       case WILC_3000:
+               if (gpio == 0 || gpio == 3 || gpio == 4 ||
+                   (gpio >= 17 && gpio <= 20))
+                       return true;
+               else
+                       return false;
+       default:
+               return false;
+       }
+}
+
+int wilc_set_antenna(struct wilc_vif *vif, u8 mode)
+{
+       struct wid wid;
+       int ret;
+       struct sysfs_attr_group *attr_syfs_p = &vif->attr_sysfs;
+       struct host_if_set_ant set_ant;
+
+       set_ant.mode = mode;
+
+       if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_INVALID_GPIO_CTRL) {
+               PRINT_ER(vif->ndev, "Ant switch GPIO mode is invalid.\n");
+               PRINT_ER(vif->ndev, "Set it using /sys/wilc/ant_swtch_mode\n");
+               return WILC_FAIL;
+       }
+
+       if (is_valid_gpio(vif, attr_syfs_p->antenna1)) {
+               set_ant.antenna1 = attr_syfs_p->antenna1;
+       } else {
+               PRINT_ER(vif->ndev, "Invalid GPIO%d\n", attr_syfs_p->antenna1);
+               return WILC_FAIL;
+       }
+
+       if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_DUAL_GPIO_CTRL) {
+               if ((attr_syfs_p->antenna2 != attr_syfs_p->antenna1) &&
+                   is_valid_gpio(vif, attr_syfs_p->antenna2)) {
+                       set_ant.antenna2 = attr_syfs_p->antenna2;
+               } else {
+                       PRINT_ER(vif->ndev, "Invalid GPIO %d\n",
+                                attr_syfs_p->antenna2);
+                       return WILC_FAIL;
+               }
+       }
+
+       set_ant.gpio_mode = attr_syfs_p->ant_swtch_mode;
+
+       wid.id = WID_ANTENNA_SELECTION;
+       wid.type = WID_BIN;
+       wid.val = (u8 *)&set_ant;
+       wid.size = sizeof(struct host_if_set_ant);
+       if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_SNGL_GPIO_CTRL)
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "set antenna %d on GPIO %d\n", set_ant.mode,
+                          set_ant.antenna1);
+       else if (attr_syfs_p->ant_swtch_mode == ANT_SWTCH_DUAL_GPIO_CTRL)
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "set antenna %d on GPIOs %d and %d\n",
+                          set_ant.mode, set_ant.antenna1,
+                          set_ant.antenna2);
+
+       ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1,
+                                  wilc_get_vif_idx(vif));
+       if (ret)
+               PRINT_ER(vif->ndev, "Failed to set antenna mode\n");
+
+       return ret;
+}
diff --git a/drivers/net/wireless/mchp/host_interface.h b/drivers/net/wireless/mchp/host_interface.h
new file mode 100644 (file)
index 0000000..92c1c17
--- /dev/null
@@ -0,0 +1,269 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef HOST_INT_H
+#define HOST_INT_H
+#include <linux/ieee80211.h>
+#include "wilc_wlan_if.h"
+
+enum {
+       WILC_IDLE_MODE = 0x0,
+       WILC_AP_MODE = 0x1,
+       WILC_STATION_MODE = 0x2,
+       WILC_GO_MODE = 0x3,
+       WILC_CLIENT_MODE = 0x4,
+       WILC_MONITOR_MODE = 0x5
+};
+
+enum {
+       WILC_P2P_IFC = 0x00,
+       WILC_WLAN_IFC = 0x01,
+};
+
+#define IFC_0 "wlan0"
+#define IFC_1 "p2p0"
+
+#define WILC_MAX_NUM_STA                       9
+#define WILC_MAX_NUM_SCANNED_CH                        14
+#define WILC_MAX_NUM_PROBED_SSID               10
+
+#define WILC_TX_MIC_KEY_LEN                    8
+#define WILC_RX_MIC_KEY_LEN                    8
+
+#define WILC_MAX_NUM_PMKIDS                    16
+#define WILC_ADD_STA_LENGTH                    40
+#define WILC_NUM_CONCURRENT_IFC                        2
+
+enum {
+       WILC_SET_CFG = 0,
+       WILC_GET_CFG
+};
+
+#define WILC_MAX_ASSOC_RESP_FRAME_SIZE   256
+extern uint32_t cfg_packet_timeout;
+
+struct assoc_resp {
+       __le16 capab_info;
+       __le16 status_code;
+       __le16 aid;
+} __packed;
+
+struct rf_info {
+       u8 link_speed;
+       s8 rssi;
+       u32 tx_cnt;
+       u32 rx_cnt;
+       u32 tx_fail_cnt;
+};
+
+enum host_if_state {
+       HOST_IF_IDLE                    = 0,
+       HOST_IF_SCANNING                = 1,
+       HOST_IF_CONNECTING              = 2,
+       HOST_IF_WAITING_CONN_RESP       = 3,
+       HOST_IF_CONNECTED               = 4,
+       HOST_IF_P2P_LISTEN              = 5,
+       HOST_IF_FORCE_32BIT             = 0xFFFFFFFF
+};
+
+struct wilc_pmkid {
+       u8 bssid[ETH_ALEN];
+       u8 pmkid[WLAN_PMKID_LEN];
+} __packed;
+
+struct wilc_pmkid_attr {
+       u8 numpmkid;
+       struct wilc_pmkid pmkidlist[WILC_MAX_NUM_PMKIDS];
+} __packed;
+
+struct cfg_param_attr {
+       u32 flag;
+       u16 short_retry_limit;
+       u16 long_retry_limit;
+       u16 frag_threshold;
+       u16 rts_threshold;
+};
+
+enum cfg_param {
+       WILC_CFG_PARAM_RETRY_SHORT = BIT(0),
+       WILC_CFG_PARAM_RETRY_LONG = BIT(1),
+       WILC_CFG_PARAM_FRAG_THRESHOLD = BIT(2),
+       WILC_CFG_PARAM_RTS_THRESHOLD = BIT(3)
+};
+
+enum scan_event {
+       SCAN_EVENT_NETWORK_FOUND        = 0,
+       SCAN_EVENT_DONE                 = 1,
+       SCAN_EVENT_ABORTED              = 2,
+       SCAN_EVENT_FORCE_32BIT          = 0xFFFFFFFF
+};
+
+enum conn_event {
+       EVENT_CONN_RESP         = 0,
+       EVENT_DISCONN_NOTIF     = 1,
+       EVENT_FORCE_32BIT               = 0xFFFFFFFF
+};
+
+enum {
+       WILC_HIF_SDIO = 0,
+       WILC_HIF_SPI = BIT(0),
+       WILC_HIF_SDIO_GPIO_IRQ = BIT(1)
+};
+
+enum {
+       WILC_MAC_STATUS_INIT = -1,
+       WILC_MAC_STATUS_DISCONNECTED = 0,
+       WILC_MAC_STATUS_CONNECTED = 1
+};
+
+struct wilc_rcvd_net_info {
+       s8 rssi;
+       u8 ch;
+       u16 frame_len;
+       struct ieee80211_mgmt *mgmt;
+};
+
+typedef void (*wilc_remain_on_chan_ready)(void *);
+
+struct wilc_user_scan_req {
+       void (*scan_result)(enum scan_event evt,
+                           struct wilc_rcvd_net_info *info, void *priv);
+       void *arg;
+       u32 ch_cnt;
+};
+
+struct wilc_conn_info {
+       u8 bssid[ETH_ALEN];
+       u8 security;
+       enum authtype auth_type;
+       u8 ch;
+       u8 *req_ies;
+       size_t req_ies_len;
+       u8 *resp_ies;
+       u16 resp_ies_len;
+       u16 status;
+       void (*conn_result)(enum conn_event evt, u8 status, void *priv);
+       void *arg;
+       void *param;
+};
+
+struct wilc_remain_ch {
+       u16 ch;
+       u32 duration;
+       void (*expired)(void *priv, u64 cookie);
+       void *arg;
+       u64 cookie;
+};
+
+struct host_if_drv {
+       struct wilc_user_scan_req usr_scan_req;
+       struct wilc_conn_info conn_info;
+       struct wilc_remain_ch remain_on_ch;
+       u64 p2p_timeout;
+
+       enum host_if_state hif_state;
+
+       u8 assoc_bssid[ETH_ALEN];
+       struct completion comp_test_key_block;
+       struct completion comp_test_disconn_block;
+       struct completion comp_get_rssi;
+       struct completion comp_inactive_time;
+
+       struct timer_list scan_timer;
+       struct wilc_vif *scan_timer_vif;
+
+       struct timer_list connect_timer;
+       struct wilc_vif *connect_timer_vif;
+
+       struct timer_list remain_on_ch_timer;
+       struct wilc_vif *remain_on_ch_timer_vif;
+
+       bool ifc_up;
+       int driver_handler_id;
+       u8 assoc_resp[WILC_MAX_ASSOC_RESP_FRAME_SIZE];
+};
+
+struct wilc_vif;
+
+signed int wilc_send_buffered_eap(struct wilc_vif *vif,
+                                 void (*deliver_to_stack)(struct wilc_vif *,
+                                                          u8 *, u32, u32, u8),
+                                 void (*eap_buf_param)(void *), u8 *buff,
+                                 unsigned int size, unsigned int pkt_offset,
+                                 void *user_arg);
+int wilc_remove_wep_key(struct wilc_vif *vif, u8 index);
+int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index);
+int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
+                            u8 index);
+int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
+                           u8 index, u8 mode, enum authtype auth_type);
+int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
+                const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic,
+                u8 mode, u8 cipher_mode, u8 index);
+s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac,
+                          u32 *out_val);
+int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
+                   u8 index, u32 key_rsc_len, const u8 *key_rsc,
+                   const u8 *rx_mic, const u8 *tx_mic, u8 mode,
+                   u8 cipher_mode);
+int wilc_set_pmkid_info(struct wilc_vif *vif, struct wilc_pmkid_attr *pmkid);
+int wilc_get_mac_address(struct wilc_vif *vif, u8 *mac_addr);
+int wilc_set_mac_address(struct wilc_vif *vif, u8 *mac_addr);
+int wilc_set_join_req(struct wilc_vif *vif, u8 *bssid, const u8 *ies,
+                     size_t ies_len);
+int wilc_disconnect(struct wilc_vif *vif);
+int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel);
+int wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level);
+int wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type,
+             u8 *ch_freq_list, u8 ch_list_len,
+             void (*scan_result_fn)(enum scan_event,
+                                    struct wilc_rcvd_net_info *, void *),
+             void *user_arg, struct cfg80211_scan_request *request);
+int wilc_hif_set_cfg(struct wilc_vif *vif,
+                    struct cfg_param_attr *cfg_param);
+int wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler);
+int wilc_deinit(struct wilc_vif *vif);
+int wilc_add_beacon(struct wilc_vif *vif, u32 interval, u32 dtim_period,
+                   struct cfg80211_beacon_data *params);
+int wilc_del_beacon(struct wilc_vif *vif);
+int wilc_add_station(struct wilc_vif *vif, const u8 *mac,
+                    struct station_parameters *params);
+int wilc_del_allstation(struct wilc_vif *vif, u8 mac_addr[][ETH_ALEN]);
+int wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr);
+int wilc_edit_station(struct wilc_vif *vif, const u8 *mac,
+                     struct station_parameters *params);
+int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout);
+int wilc_setup_multicast_filter(struct wilc_vif *vif, u32 enabled, u32 count,
+                               u8 *mc_list);
+int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie,
+                          u32 duration, u16 chan,
+                          void (*expired)(void *, u64), void *user_arg);
+int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie);
+void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg);
+int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
+                            u8 ifc_id);
+int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode);
+int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats);
+void wilc_resolve_disconnect_aberration(struct wilc_vif *vif);
+int wilc_get_vif_idx(struct wilc_vif *vif);
+int wilc_set_tx_power(struct wilc_vif *vif, u8 tx_power);
+int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power);
+/*0 select antenna 1 , 2 select antenna mode , 2 allow the firmware to choose
+ * the best antenna
+ */
+int wilc_set_antenna(struct wilc_vif *vif, u8 mode);
+
+void wilc_set_wowlan_trigger(struct wilc_vif *vif, u8 wowlan_trigger);
+
+extern u8 wilc_initialized;
+s32 handle_scan_done(struct wilc_vif *vif, enum scan_event evt);
+void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length);
+void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length);
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length);
+void *wilc_parse_join_bss_param(struct cfg80211_bss *bss,
+                               struct cfg80211_crypto_settings *crypto);
+void wilc_powersave_state_changes(struct wilc_vif *vif);
+#endif
diff --git a/drivers/net/wireless/mchp/microchip,wilc,sdio.txt b/drivers/net/wireless/mchp/microchip,wilc,sdio.txt
new file mode 100644 (file)
index 0000000..028c640
--- /dev/null
@@ -0,0 +1,36 @@
+* Microchip WILC wireless SDIO device
+
+The wilc1000 chips can be connected via SDIO. The node is used to specifiy
+child node to the SDIO controller that connects the device to the system.
+
+Required properties:
+- compatible   :       Should be "microchip,wilc1000-sdio"
+- irq-gpios    :       Connect to a host IRQ
+- reset-gpios  :       Reset module GPIO
+- chip_en-gpios        :       Chip enable GPIO
+- reg          :       Slot ID used in the controller
+
+Optional:
+- bus-width    :       Number of data lines wired up the slot. Default 1 bit.
+
+
+Examples:
+mmc1: mmc@fc000000 {
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_mmc1_clk_cmd_dat0 &pinctrl_mmc1_dat1_3>;
+               non-removable;
+               vmmc-supply = <&vcc_mmc1_reg>;
+               vqmmc-supply = <&vcc_3v3_reg>;
+               status = "okay";
+
+               wilc_sdio@0 {
+                       compatible = "microchip,wilc1000", "microchip,wilc3000";
+                       irq-gpios = <&pioC 27 0>;
+                       reset-gpios = <&pioB 28 0>;
+                       chip_en-gpios = <&pioC 30 0>;
+                       status = "okay";
+                       reg = <0>;
+                       bus-width = <4>;
+               }
+       };
+}
diff --git a/drivers/net/wireless/mchp/microchip,wilc,spi.txt b/drivers/net/wireless/mchp/microchip,wilc,spi.txt
new file mode 100644 (file)
index 0000000..2e2a930
--- /dev/null
@@ -0,0 +1,30 @@
+* Microchip WILC wireless SPI device
+
+The wilc1000 chips can be connected via SPI. This document describes
+the binding for the SPI connected module.
+
+Required properties:
+- compatible           : Should be "microchip,wilc1000-spi"
+- spi-max-frequency    : Maximum SPI clocking speed of device in Hz
+- reg                  : Chip select address of device
+- irq-gpios            : Connect to a host IRQ
+- reset-gpios          : Reset module GPIO
+- chip_en-gpios:       : Chip enable GPIO
+
+
+Examples:
+
+spi1: spi@fc018000 {
+               cs-gpios = <&pioB 21 0>;
+               status = "okay";
+
+               wilc_spi@0 {
+                       compatible = "microchip,wilc1000", "microchip,wilc3000";
+                       spi-max-frequency = <48000000>;
+                       reg = <0>;
+                       irq-gpios = <&pioC 27 0>;
+                       reset-gpios = <&pioB 28 0>;
+                       chip_en-gpios = <&pioC 30 0>;
+                       status = "okay";
+               };
+};
diff --git a/drivers/net/wireless/mchp/sysfs.c b/drivers/net/wireless/mchp/sysfs.c
new file mode 100644 (file)
index 0000000..8d32e53
--- /dev/null
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/kobject.h>
+#include "wilc_wfi_cfgoperations.h"
+
+static struct kobject *wilc_kobj;
+static int device_created;
+static struct wilc_vif *vif[WILC_NUM_CONCURRENT_IFC];
+
+static ssize_t wilc_sysfs_show(struct kobject *kobj,
+                              struct kobj_attribute *attr, char *buf)
+{
+       int attr_val = -1;
+
+       if (strcmp(attr->attr.name, "p2p_mode") == 0)
+               attr_val = vif[0]->attr_sysfs.p2p_mode;
+       if (strcmp(attr->attr.name, "ant_swtch_mode") == 0)
+               attr_val = vif[0]->attr_sysfs.ant_swtch_mode;
+       else if (strcmp(attr->attr.name, "antenna1") == 0)
+               attr_val = vif[0]->attr_sysfs.antenna1;
+       else if (strcmp(attr->attr.name, "antenna2") == 0)
+               attr_val = vif[0]->attr_sysfs.antenna2;
+
+       return sprintf(buf, "%d\n", attr_val);
+}
+
+static ssize_t wilc_sysfs_store(struct kobject *kobj,
+                               struct kobj_attribute *attr, const char *buf,
+                               size_t count)
+{
+       int attr_val;
+       int i;
+
+       for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) {
+               if (kstrtoint(buf, 10, &attr_val))
+                       PRINT_ER(vif[i]->ndev,
+                                "Failed to convert p2p_mode string");
+               if (strcmp(attr->attr.name, "p2p_mode") == 0) {
+                       vif[i]->attr_sysfs.p2p_mode = (attr_val?1:0);
+               } else if (strcmp(attr->attr.name, "ant_swtch_mode") == 0) {
+                       if (attr_val > ANT_SWTCH_DUAL_GPIO_CTRL)
+                               PRINT_ER(vif[i]->ndev,
+                                        "Valid antenna switch modes:\n1-Single Antenna, 2-Dual Antenna\n");
+                       else
+                               vif[i]->attr_sysfs.ant_swtch_mode = attr_val;
+               } else if (strcmp(attr->attr.name, "antenna1") == 0) {
+                       vif[i]->attr_sysfs.antenna1 = attr_val;
+               } else if (strcmp(attr->attr.name, "antenna2") == 0) {
+                       vif[i]->attr_sysfs.antenna2 = attr_val;
+               }
+       }
+
+       return count;
+}
+
+static struct kobj_attribute p2p_mode_attr =
+       __ATTR(p2p_mode, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+static struct kobj_attribute ant_swtch_mode_attr =
+       __ATTR(ant_swtch_mode, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+static struct kobj_attribute ant_swtch_antenna1_attr =
+       __ATTR(antenna1, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+static struct kobj_attribute ant_swtch_antenna2_attr =
+       __ATTR(antenna2, 0664, wilc_sysfs_show, wilc_sysfs_store);
+
+
+static struct attribute *wilc_attrs[] = {
+       &p2p_mode_attr.attr,
+       &ant_swtch_mode_attr.attr,
+       &ant_swtch_antenna1_attr.attr,
+       &ant_swtch_antenna2_attr.attr,
+       NULL
+};
+
+static struct attribute_group attr_group = {
+       .attrs = wilc_attrs,
+};
+
+void wilc_sysfs_init(struct wilc_vif *vif1, struct wilc_vif *vif2)
+{
+       int retval;
+       int i;
+
+       vif[0] = vif1;
+       vif[1] = vif2;
+
+       if (device_created)
+               return;
+
+       wilc_kobj = kobject_create_and_add("wilc", NULL);
+       if (!wilc_kobj) {
+               retval = -ENOMEM;
+               return;
+       }
+
+       for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) {
+               /* By default p2p mode is Group Owner */
+               vif[i]->attr_sysfs.p2p_mode = 1;
+               vif[i]->attr_sysfs.ant_swtch_mode = ANT_SWTCH_INVALID_GPIO_CTRL;
+               vif[i]->attr_sysfs.antenna1 = 0xFF;
+               vif[i]->attr_sysfs.antenna2 = 0xFF;
+       }
+       retval = sysfs_create_group(wilc_kobj, &attr_group);
+       device_created = 1;
+}
+
+void wilc_sysfs_exit(void)
+{
+       device_created = 0;
+       sysfs_remove_group(wilc_kobj, &attr_group);
+       kobject_put(wilc_kobj);
+}
+
diff --git a/drivers/net/wireless/mchp/wilc_bt.c b/drivers/net/wireless/mchp/wilc_bt.c
new file mode 100644 (file)
index 0000000..a92a34b
--- /dev/null
@@ -0,0 +1,691 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/firmware.h>
+
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+
+#include "wilc_wfi_netdevice.h"
+
+static struct wilc *wilc_bt;
+static dev_t chc_dev_no; /* Global variable for the first device number */
+static struct cdev str_chc_dev; /* Global variable for the character */
+struct device *dev;
+static struct class *chc_dev_class; /* Global variable for the device class */
+static bool device_created;
+int bt_init_done;
+
+typedef void (wilc_cmd_handler)(char *);
+
+static void handle_cmd_bt_enable(char *param);
+static void handle_cmd_pwr_up(char *param);
+static void handle_cmd_pwr_down(char *param);
+static void handle_cmd_chip_wake_up(char *param);
+static void handle_cmd_chip_allow_sleep(char *param);
+static void handle_cmd_download_fw(char *param);
+static void handle_cmd_cca_thrshld(char *param);
+
+static void wilc_bt_firmware_download(struct wilc *);
+static void wilc_bt_start(struct wilc *);
+static int wilc_bt_dev_open(struct inode *i, struct file *f);
+static int wilc_bt_dev_close(struct inode *i, struct file *f);
+static ssize_t wilc_bt_dev_read(struct file *f, char __user *buf, size_t len,
+                               loff_t *off);
+static ssize_t wilc_bt_dev_write(struct file *f, const char __user *buff,
+                                size_t len, loff_t *off);
+
+static const struct file_operations pugs_fops = {
+       .owner = THIS_MODULE,
+       .open = wilc_bt_dev_open,
+       .release = wilc_bt_dev_close,
+       .read = wilc_bt_dev_read,
+       .write = wilc_bt_dev_write
+};
+
+struct cmd_entry {
+       const char *str;
+       wilc_cmd_handler *wilc_handle_cmd;
+};
+
+static const struct cmd_entry cmd_table[] = {
+       {"BT_DOWNLOAD_FW", handle_cmd_download_fw},
+       {"BT_POWER_UP", handle_cmd_pwr_up},
+       {"BT_POWER_DOWN", handle_cmd_pwr_down},
+       {"BT_FW_CHIP_WAKEUP", handle_cmd_chip_wake_up},
+       {"BT_FW_CHIP_ALLOW_SLEEP", handle_cmd_chip_allow_sleep},
+       {"BT_ENABLE", handle_cmd_bt_enable},
+       {"CCA_THRESHOLD", handle_cmd_cca_thrshld},
+       /* Keep the NULL handler at the end of the table */
+       {(const char *)NULL, NULL},
+};
+
+static int wilc_bt_dev_open(struct inode *i, struct file *f)
+{
+       pr_err("at_pwr_dev: open()\n");
+       return 0;
+}
+
+static int wilc_bt_dev_close(struct inode *i, struct file *f)
+{
+       pr_info("at_pwr_dev: close()\n");
+       return 0;
+}
+
+static ssize_t wilc_bt_dev_read(struct file *f, char __user *buf, size_t len,
+                               loff_t *off)
+{
+       pr_debug("at_pwr_dev: read()\n");
+       return 0;
+}
+
+static ssize_t wilc_bt_dev_write(struct file *f, const char __user *buff,
+                                size_t len, loff_t *off)
+{
+       struct cmd_entry *cmd;
+       char *usr_str;
+
+
+       if (len == 0) {
+               pr_debug("received invalid size <=0: %d\n", len);
+               return len;
+       }
+
+       usr_str = kmalloc(len, GFP_KERNEL);
+
+       if (copy_from_user(usr_str, buff, len))
+               return -EIO;
+
+       pr_debug("received %s, len %d\n", usr_str, len);
+       // call the appropriate command handler
+       cmd = (struct cmd_entry *)cmd_table;
+       while (cmd->wilc_handle_cmd != NULL) {
+               if (strncmp(cmd->str, usr_str, strlen(cmd->str)) == 0) {
+                       pr_debug("param len: %d, string: %s\n",
+                                len - strlen(cmd->str), usr_str);
+                       cmd->wilc_handle_cmd(usr_str + strlen(cmd->str));
+                       break;
+               }
+               cmd++;
+       }
+
+       kfree(usr_str);
+       return len;
+}
+
+static void wilc_bt_create_device(void)
+{
+       int ret = 0;
+
+       if (device_created)
+               return;
+
+       ret = alloc_chrdev_region(&chc_dev_no, 0, 1, "atmel");
+       if (ret < 0)
+               return;
+       chc_dev_class = class_create(THIS_MODULE, "atmel");
+       if (IS_ERR(chc_dev_class)) {
+               unregister_chrdev_region(chc_dev_no, 1);
+               return;
+       }
+       dev = device_create(chc_dev_class, NULL, chc_dev_no, NULL,
+                           "wilc_bt");
+       if (IS_ERR(dev)) {
+               class_destroy(chc_dev_class);
+               unregister_chrdev_region(chc_dev_no, 1);
+               return;
+       }
+
+       cdev_init(&str_chc_dev, &pugs_fops);
+       ret = cdev_add(&str_chc_dev, chc_dev_no, 1);
+       if (ret < 0) {
+               device_destroy(chc_dev_class, chc_dev_no);
+               class_destroy(chc_dev_class);
+               unregister_chrdev_region(chc_dev_no, 1);
+               return;
+       }
+       mutex_init(&wilc_bt->cs);
+       device_created = 1;
+}
+
+static void handle_cmd_cca_thrshld(char *param)
+{
+       int carrier_thrshld, noise_thrshld;
+       unsigned int carr_thrshld_frac, noise_thrshld_frac, carr_thrshld_int,
+               noise_thrshld_int, reg;
+
+       if (param == NULL) {
+               pr_err("Invalid parameter\n");
+               return;
+       }
+
+       if (sscanf(param, " %d %d", &noise_thrshld, &carrier_thrshld) != 2) {
+               pr_err("Failed to parse input parameters. Usage:\n");
+               pr_err("echo CCA_THRESHOLD NOISE_THRESHOLD CARRIER_THRESHOLD > /dev/at_pwr_dev\n");
+               pr_err("where threshold values are in dB * 10\n");
+               pr_err("e.g. echo CCA_THRESHOLD -625 -826 > /dev/at_pwr_dev to set thresholds to -62.5 and -82.6\n\n");
+               return;
+       }
+
+       pr_info("Changing CCA noise threshold to %d and carrier thresholds to %d\n",
+               noise_thrshld, carrier_thrshld);
+
+       carr_thrshld_int = carrier_thrshld/10;
+       if (carrier_thrshld < 0)
+               carr_thrshld_frac = (carr_thrshld_int * 10) - carrier_thrshld;
+       else
+               carr_thrshld_frac = carrier_thrshld - (carr_thrshld_int * 10);
+
+       noise_thrshld_int = noise_thrshld/10;
+       if (noise_thrshld < 0)
+               noise_thrshld_frac = (noise_thrshld_int * 10) - noise_thrshld;
+       else
+               noise_thrshld_frac = noise_thrshld - (noise_thrshld_int * 10);
+
+       wilc_bt->hif_func->hif_read_reg(wilc_bt, CCA_CTL_2, &reg);
+       reg &= ~(0x7FF0000);
+       reg |= ((noise_thrshld_frac & 0x7) | ((noise_thrshld_int & 0x1FF)
+                                             << 3)) << 16;
+       wilc_bt->hif_func->hif_write_reg(wilc_bt, CCA_CTL_2, reg);
+
+       wilc_bt->hif_func->hif_read_reg(wilc_bt, CCA_CTL_7, &reg);
+       reg &= ~(0x7FF0000);
+       reg |= ((carr_thrshld_frac & 0x7) | ((carr_thrshld_int & 0x1FF) << 3))
+               << 16;
+       wilc_bt->hif_func->hif_write_reg(wilc_bt, CCA_CTL_7, reg);
+}
+
+int wilc_bt_power_down(struct wilc *wilc, int source)
+{
+       const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+       if (source == DEV_BT) {
+               int ret;
+               u32 reg;
+
+               pr_info("AT PWR: bt_power_down\n");
+
+               /* Adjust coexistence module. This should be done from the FW
+                * in the future
+                */
+               acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+               ret = hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL,
+                                            &reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail read reg %x\n",
+                              GLOBAL_MODE_CONTROL);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+               /* Clear BT mode*/
+               reg &= ~BIT(1);
+               ret = hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL,
+                                             reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail write reg %x\n",
+                              GLOBAL_MODE_CONTROL);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+
+
+               /*TicketId1115*/
+               /*Disable awake coex null frames*/
+               ret = hif_func->hif_read_reg(wilc, COE_AUTO_PS_ON_NULL_PKT,
+                                            &reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail read reg %x\n",
+                              COE_AUTO_PS_ON_NULL_PKT);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+               reg &= ~BIT(30);
+               ret = hif_func->hif_write_reg(wilc, COE_AUTO_PS_ON_NULL_PKT,
+                                             reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail write reg %x\n",
+                              COE_AUTO_PS_ON_NULL_PKT);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+
+               /*TicketId1115*/
+               /*Disable doze coex null frames*/
+               ret = hif_func->hif_read_reg(wilc, COE_AUTO_PS_OFF_NULL_PKT,
+                                            &reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail read reg %x\n",
+                              COE_AUTO_PS_OFF_NULL_PKT);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+               reg &= ~BIT(30);
+               ret = hif_func->hif_write_reg(wilc, COE_AUTO_PS_OFF_NULL_PKT,
+                                             reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail write reg %x\n",
+                              COE_AUTO_PS_OFF_NULL_PKT);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+               // Disable BT wakeup
+               ret = hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL,
+                                            &reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail read reg %x\n",
+                              PWR_SEQ_MISC_CTRL);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+               reg &= ~BIT(29);
+               ret = hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL,
+                                             reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail write reg %x\n",
+                              PWR_SEQ_MISC_CTRL);
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+                       return ret;
+               }
+
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+
+               bt_init_done = 0;
+       }
+
+       mutex_lock(&wilc->cs);
+
+       pr_info("source: %s, current bus status Wifi: %d, BT: %d\n",
+                (source == DEV_WIFI ? "Wifi" : "BT"),
+                wilc->power_status[DEV_WIFI],
+                wilc->power_status[DEV_BT]);
+
+       if (wilc->power_status[source] == false) {
+               pr_err("power down request for already powered down source %s\n",
+                      (source == DEV_WIFI ? "Wifi" : "BT"));
+       } else if (((source == DEV_WIFI) &&
+                 (wilc->power_status[DEV_BT] == true)) ||
+                 ((source == DEV_BT) &&
+                 (wilc->power_status[DEV_WIFI] == true))) {
+               pr_warn("Another device is preventing power down. request source is %s\n",
+                       (source == DEV_WIFI ? "Wifi" : "BT"));
+       } else {
+               wilc_wlan_power_off_sequence(wilc);
+       }
+       wilc->power_status[source] = false;
+
+       mutex_unlock(&wilc->cs);
+
+       return 0;
+}
+
+int wilc_bt_power_up(struct wilc *wilc, int source)
+{
+       int count = 0;
+       int ret;
+       int reg;
+       const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+       mutex_lock(&wilc->cs);
+
+       pr_debug("source: %s, current bus status Wifi: %d, BT: %d\n",
+                (source == DEV_WIFI ? "Wifi" : "BT"),
+                wilc->power_status[DEV_WIFI],
+                wilc->power_status[DEV_BT]);
+
+       if (wilc->power_status[source] == true) {
+               pr_err("power up request for already powered up source %s\n",
+                        (source == DEV_WIFI ? "Wifi" : "BT"));
+       } else {
+               /*Bug 215*/
+               /*Avoid overlapping between BT and Wifi intialization*/
+               if (wilc->power_status[DEV_WIFI] == true) {
+                       while (!wilc->initialized) {
+                               msleep(100);
+                               if (++count > 20) {
+                                       pr_warn("Wifi initialize timeout\n");
+                                       break;
+                               }
+                       }
+               } else if (wilc->power_status[DEV_BT] == true) {
+                       while (!bt_init_done) {
+                               msleep(200);
+                               if (++count > 30) {
+                                       pr_warn("BT initialize timeout\n");
+                                       break;
+                               }
+                       }
+                       /* An additional wait to give BT firmware time to do
+                        * CPLL update as the time measured since the start of
+                        * BT Fw till the end of function "rf_nmi_init_tuner"
+                        * was 71.2 ms
+                        */
+                       msleep(100);
+               }
+       }
+
+       if ((wilc->power_status[DEV_WIFI] == true) ||
+                  (wilc->power_status[DEV_BT] == true)) {
+               pr_info("Device already up. request source is %s\n",
+                        (source == DEV_WIFI ? "Wifi" : "BT"));
+       } else {
+               pr_info("WILC POWER UP\n");
+       }
+       wilc->power_status[source] = true;
+       mutex_unlock(&wilc->cs);
+
+       if (source == DEV_BT) {
+               /*TicketId1092*/
+               /*If WiFi is off, force BT*/
+               if (wilc->power_status[DEV_WIFI] == false) {
+                       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+                       /*TicketId1115*/
+                       /*Disable awake coex null frames*/
+                       ret = hif_func->hif_read_reg(wilc,
+                                                    COE_AUTO_PS_ON_NULL_PKT,
+                                                    &reg);
+                       if (!ret) {
+                               pr_err("[wilc start]: fail read reg %x\n",
+                                      COE_AUTO_PS_ON_NULL_PKT);
+                               goto fail;
+                       }
+                       reg &= ~BIT(30);
+                       ret = hif_func->hif_write_reg(wilc,
+                                                     COE_AUTO_PS_ON_NULL_PKT,
+                                                     reg);
+                       if (!ret) {
+                               pr_err("[wilc start]: fail write reg %x\n",
+                                      COE_AUTO_PS_ON_NULL_PKT);
+                               goto fail;
+                       }
+
+                       /*TicketId1115*/
+                       /*Disable doze coex null frames*/
+                       ret = hif_func->hif_read_reg(wilc,
+                                                    COE_AUTO_PS_OFF_NULL_PKT,
+                                                    &reg);
+                       if (!ret) {
+                               pr_err("[wilc start]: fail read reg %x\n",
+                                      COE_AUTO_PS_OFF_NULL_PKT);
+                               goto fail;
+                       }
+                       reg &= ~BIT(30);
+                       ret = hif_func->hif_write_reg(wilc,
+                                                     COE_AUTO_PS_OFF_NULL_PKT,
+                                                     reg);
+                       if (!ret) {
+                               pr_err("[wilc start]: fail write reg %x\n",
+                                      COE_AUTO_PS_OFF_NULL_PKT);
+                               goto fail;
+                       }
+
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+               }
+
+               // Enable BT wakeup
+               acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+               ret = hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL,
+                                            &reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail read reg %x ...\n",
+                              PWR_SEQ_MISC_CTRL);
+                       goto fail;
+               }
+               reg |= BIT(29);
+               ret = hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL,
+                                             reg);
+               if (!ret) {
+                       pr_err("[wilc start]: fail write reg %x ...\n",
+                              PWR_SEQ_MISC_CTRL);
+                       goto fail;
+               }
+
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+       }
+
+       return 0;
+
+fail:
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+       wilc_bt_power_down(wilc, DEV_BT);
+       return ret;
+}
+
+static void wilc_bt_firmware_download(struct wilc *wilc)
+{
+       u32 offset;
+       u32 addr, size, size2, blksz;
+       u8 *dma_buffer;
+       const struct firmware *wilc_bt_firmware;
+       const u8 *buffer;
+       size_t buffer_size;
+       int ret = 0;
+       u32 reg;
+       const struct wilc_hif_func *hif_func;
+
+       hif_func = wilc->hif_func;
+
+       pr_info("Bluetooth firmware: %s\n", FW_WILC3000_BLE);
+       if (request_firmware(&wilc_bt_firmware, FW_WILC3000_BLE, dev) != 0) {
+               pr_err("%s - firmare not available. Skip!\n", FW_WILC3000_BLE);
+               ret = -1;
+               goto fail_1;
+       }
+
+       buffer = wilc_bt_firmware->data;
+       buffer_size = (size_t)wilc_bt_firmware->size;
+       if (buffer_size <= 0) {
+               pr_err("Firmware size = 0!\n");
+               ret = -1;
+               goto fail_1;
+       }
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+       ret = hif_func->hif_write_reg(wilc, 0x4f0000, 0x71);
+       if (!ret) {
+               pr_err("[wilc start]: fail write reg 0x4f0000 ...\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+               goto fail_1;
+       }
+
+       /*
+        * Avoid booting from BT boot ROM. Make sure that Drive IRQN
+        * [SDIO platform] or SD_DAT3 [SPI platform] to ?1?
+        */
+       /* Set cortus reset register to register control. */
+       ret = hif_func->hif_read_reg(wilc, 0x3b0090, &reg);
+       if (!ret) {
+               pr_err("[wilc start]: fail read reg 0x3b0090 ...\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+               goto fail_1;
+       }
+
+       reg |= (1 << 0);
+       ret = hif_func->hif_write_reg(wilc, 0x3b0090, reg);
+       if (!ret) {
+               pr_err("[wilc start]: fail write reg 0x3b0090 ...\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+               goto fail_1;
+       }
+
+       hif_func->hif_read_reg(wilc, 0x3B0400, &reg);
+
+       if (reg & (1ul << 2)) {
+               reg &= ~(1ul << 2);
+       } else {
+               reg |= (1ul << 2);
+               hif_func->hif_write_reg(wilc, 0x3B0400, reg);
+               reg &= ~(1ul << 2);
+       }
+       hif_func->hif_write_reg(wilc, 0x3B0400, reg);
+
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+
+       /* blocks of sizes > 512 causes the wifi to hang! */
+       blksz = (1ul << 9);
+       /* Allocate a DMA coherent  buffer. */
+       dma_buffer = kmalloc(blksz, GFP_KERNEL);
+       if (dma_buffer == NULL) {
+               ret = -5;
+               pr_err("Can't allocate buffer for BT firmware download IO error\n");
+               goto fail_1;
+       }
+       pr_info("Downloading BT firmware size = %d ...\n", buffer_size);
+
+       offset = 0;
+       addr = 0x400000;
+       size = buffer_size;
+       addr = cpu_to_le32(addr);
+       size = cpu_to_le32(size);
+       offset = 0;
+
+       while (((int)size) && (offset < buffer_size)) {
+               if (size <= blksz)
+                       size2 = size;
+               else
+                       size2 = blksz;
+
+               /* Copy firmware into a DMA coherent buffer */
+               memcpy(dma_buffer, &buffer[offset], size2);
+
+               acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+               ret = hif_func->hif_block_tx(wilc, addr, dma_buffer, size2);
+
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+
+               if (!ret)
+                       break;
+
+               addr += size2;
+               offset += size2;
+               size -= size2;
+       }
+
+       if (!ret) {
+               ret = -5;
+               pr_err("Can't download BT firmware IO error\n");
+               goto fail;
+       }
+
+fail:
+       kfree(dma_buffer);
+fail_1:
+       pr_debug("Freeing BT FW buffer ...\n");
+       pr_debug("Releasing BT firmware\n");
+       release_firmware(wilc_bt_firmware);
+}
+
+static void wilc_bt_start(struct wilc *wilc)
+{
+       u32 val32 = 0;
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT);
+
+       pr_info("Starting BT firmware\n");
+       /*
+        * Write the firmware download complete magic value 0x10ADD09E at
+        * location 0xFFFF000C (Cortus map) or C000C (AHB map).
+        * This will let the boot-rom code execute from RAM.
+        */
+       wilc->hif_func->hif_write_reg(wilc, 0x4F000c, 0x10add09e);
+
+       wilc->hif_func->hif_read_reg(wilc, 0x3B0400, &val32);
+       val32 &= ~((1ul << 2) | (1ul << 3));
+       wilc->hif_func->hif_write_reg(wilc, 0x3B0400, val32);
+
+       msleep(100);
+
+       val32 |= ((1ul << 2) | (1ul << 3));
+
+       wilc->hif_func->hif_write_reg(wilc, 0x3B0400, val32);
+
+       pr_info("BT Start Succeeded\n");
+
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT);
+}
+
+static void handle_cmd_pwr_up(char *param)
+{
+       pr_info("AT PWR: bt_power_up\n");
+       bt_init_done = 0;
+
+       if (!wilc_bt->initialized && !wilc_bt->hif_func->hif_is_init(wilc_bt)) {
+               acquire_bus(wilc_bt, WILC_BUS_ACQUIRE_ONLY, DEV_BT);
+               if (!wilc_bt->hif_func->hif_init(wilc_bt, false)) {
+                       release_bus(wilc_bt, WILC_BUS_RELEASE_ONLY, DEV_BT);
+                       return;
+               }
+               release_bus(wilc_bt, WILC_BUS_RELEASE_ONLY, DEV_BT);
+       }
+
+       wilc_bt_power_up(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_pwr_down(char *param)
+{
+       wilc_bt_power_down(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_chip_wake_up(char *param)
+{
+       chip_wakeup(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_chip_allow_sleep(char *param)
+{
+       bt_init_done = 1;
+       chip_allow_sleep(wilc_bt, DEV_BT);
+}
+
+static void handle_cmd_download_fw(char *param)
+{
+       pr_info("AT PWR: bt_download_fw\n");
+
+       wilc_bt_firmware_download(wilc_bt);
+       wilc_bt_start(wilc_bt);
+}
+
+static void handle_cmd_bt_enable(char *param)
+{
+       wilc_bt_power_up(wilc_bt, DEV_BT);
+       wilc_bt_firmware_download(wilc_bt);
+       wilc_bt_start(wilc_bt);
+}
+
+void wilc_bt_init(struct wilc *wilc)
+{
+       wilc_bt = wilc;
+       pr_debug("at_pwr_dev: init\n");
+       wilc_bt_create_device();
+}
+
+void wilc_bt_deinit(void)
+{
+       pr_info("at_pwr_dev: deinit\n");
+
+       if (&wilc_bt->cs != NULL)
+               mutex_destroy(&wilc_bt->cs);
+
+       cdev_del(&str_chc_dev);
+       device_created = 0;
+       device_destroy(chc_dev_class, chc_dev_no);
+       class_destroy(chc_dev_class);
+       unregister_chrdev_region(chc_dev_no, 1);
+       pr_info("at_pwr_dev: unregistered\n");
+}
diff --git a/drivers/net/wireless/mchp/wilc_debugfs.c b/drivers/net/wireless/mchp/wilc_debugfs.c
new file mode 100644 (file)
index 0000000..791d933
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include "wilc_debugfs.h"
+
+atomic_t WILC_DEBUG_REGION = ATOMIC_INIT(INIT_DBG | GENERIC_DBG |
+                                        CFG80211_DBG | HOSTAPD_DBG |
+                                        PWRDEV_DBG);
+
+#if defined(WILC_DEBUGFS)
+static struct dentry *wilc_dir;
+
+static ssize_t wilc_debug_region_read(struct file *file, char __user *userbuf,
+                                    size_t count, loff_t *ppos)
+{
+       char buf[128];
+       int res = 0;
+
+       /* only allow read from start */
+       if (*ppos > 0)
+               return 0;
+
+       res = scnprintf(buf, sizeof(buf), "Debug Region: (0x%08x)\n",
+                                   atomic_read(&WILC_DEBUG_REGION));
+
+       return simple_read_from_buffer(userbuf, count, ppos, buf, res);
+}
+
+static ssize_t wilc_debug_region_write(struct file *filp,
+                                     const char __user *buf, size_t count,
+                                     loff_t *ppos)
+{
+       int flag = 0;
+       int ret;
+
+       ret = kstrtouint_from_user(buf, count, 16, &flag);
+       if (ret)
+               return ret;
+
+       if (flag > DBG_REGION_ALL) {
+               pr_err("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n",
+                          __func__, flag, atomic_read(&WILC_DEBUG_REGION));
+               pr_err("allowed bits are 0 to 15\n");
+               return -EINVAL;
+       }
+
+       atomic_set(&WILC_DEBUG_REGION, (int)flag);
+
+       pr_info("Debug region set to %x\n", atomic_read(&WILC_DEBUG_REGION));
+
+       return count;
+}
+
+#define FOPS(_open, _read, _write, _poll) { \
+               .owner  = THIS_MODULE, \
+               .open   = (_open), \
+               .read   = (_read), \
+               .write  = (_write), \
+               .poll           = (_poll), \
+}
+
+struct wilc_debugfs_info_t {
+       const char *name;
+       int perm;
+       unsigned int data;
+       const struct file_operations fops;
+};
+
+static struct wilc_debugfs_info_t debugfs_info[] = {
+       {
+               "wilc_debug_region",
+               0666,
+               0,
+               FOPS(NULL, wilc_debug_region_read, wilc_debug_region_write,
+                    NULL),
+       },
+};
+
+int wilc_debugfs_init(void)
+{
+       int i;
+       struct wilc_debugfs_info_t *info;
+
+       wilc_dir = debugfs_create_dir("wilc", NULL);
+       if (wilc_dir == NULL) {
+               pr_err("Error creating debugfs\n");
+               return -EFAULT;
+       }
+       for (i = 0; i < ARRAY_SIZE(debugfs_info); i++) {
+               info = &debugfs_info[i];
+               debugfs_create_file(info->name,
+                                   info->perm,
+                                   wilc_dir,
+                                   &info->data,
+                                   &info->fops);
+       }
+       return 0;
+}
+
+void wilc_debugfs_remove(void)
+{
+       debugfs_remove_recursive(wilc_dir);
+}
+
+#endif
diff --git a/drivers/net/wireless/mchp/wilc_debugfs.h b/drivers/net/wireless/mchp/wilc_debugfs.h
new file mode 100644 (file)
index 0000000..cf6efd9
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_DEBUGFS_H
+#define WILC_DEBUGFS_H
+
+#include <linux/kern_levels.h>
+
+#define GENERIC_DBG            BIT(0)
+#define HOSTAPD_DBG            BIT(1)
+#define HOSTINF_DBG            BIT(2)
+#define CORECONFIG_DBG         BIT(3)
+#define CFG80211_DBG           BIT(4)
+#define INT_DBG                        BIT(5)
+#define TX_DBG                 BIT(6)
+#define RX_DBG                 BIT(7)
+#define TCP_ENH                        BIT(8)
+#define INIT_DBG               BIT(9)
+#define PWRDEV_DBG             BIT(10)
+#define DBG_REGION_ALL         (BIT(11)-1)
+
+extern atomic_t WILC_DEBUG_REGION;
+
+#define PRINT_D(netdev, region, format, ...) do { \
+       if (atomic_read(&WILC_DEBUG_REGION)&(region))\
+               netdev_dbg(netdev, "DBG [%s: %d] "format, __func__, __LINE__,\
+                  ##__VA_ARGS__); } \
+       while (0)
+
+#define PRINT_INFO(netdev, region, format, ...) do { \
+       if (atomic_read(&WILC_DEBUG_REGION)&(region))\
+               netdev_info(netdev, "INFO [%s]"format, __func__, \
+               ##__VA_ARGS__); } \
+       while (0)
+
+#define PRINT_WRN(netdev, region, format, ...) do { \
+       if (atomic_read(&WILC_DEBUG_REGION)&(region))\
+               netdev_warn(netdev, "WRN [%s: %d]"format, __func__, __LINE__,\
+                   ##__VA_ARGS__); } \
+       while (0)
+
+#define PRINT_ER(netdev, format, ...) netdev_err(netdev, "ERR [%s:%d] "format,\
+       __func__, __LINE__, ##__VA_ARGS__)
+
+int wilc_debugfs_init(void);
+void wilc_debugfs_remove(void);
+#endif /* WILC_DEBUGFS_H */
diff --git a/drivers/net/wireless/mchp/wilc_mon.c b/drivers/net/wireless/mchp/wilc_mon.c
new file mode 100644 (file)
index 0000000..8a2a0b7
--- /dev/null
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "wilc_netdev.h"
+#include "wilc_wfi_cfgoperations.h"
+
+struct wfi_rtap_hdr {
+       struct ieee80211_radiotap_header hdr;
+       u8 rate;
+} __packed;
+
+struct wfi_rtap_cb_hdr {
+       struct ieee80211_radiotap_header hdr;
+       u8 rate;
+       u8 dump;
+       u16 tx_flags;
+} __packed;
+
+#define TX_RADIOTAP_PRESENT ((1 << IEEE80211_RADIOTAP_RATE) |  \
+                            (1 << IEEE80211_RADIOTAP_TX_FLAGS))
+
+void wilc_wfi_handle_monitor_rx(struct wilc *wilc, u8 *buff, u32 size)
+{
+       struct wilc_vif *vif = 0;
+       struct sk_buff *skb = NULL;
+       struct wfi_rtap_hdr *hdr;
+       int i;
+
+       for (i = 0; i < wilc->vif_num; i++) {
+               if (wilc->vif[i]->iftype == WILC_MONITOR_MODE) {
+                       vif = wilc->vif[i];
+                       break;
+               }
+       }
+
+       if (!vif) {
+               PRINT_D(vif->ndev, HOSTAPD_DBG, "Monitor interface not up\n");
+               return;
+       }
+
+       skb = dev_alloc_skb(size + sizeof(*hdr));
+
+       if (!skb) {
+               PRINT_D(vif->ndev, HOSTAPD_DBG,
+                       "Monitor if: No memory to allocate skb");
+               return;
+       }
+#if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+       skb_put_data(skb, buff, size);
+       hdr = skb_push(skb, sizeof(*hdr));
+#else
+       memcpy(skb_put(skb, size), buff, size);
+       hdr = (struct wfi_rtap_hdr *)skb_push(skb, sizeof(*hdr));
+#endif
+       memset(hdr, 0, sizeof(*hdr));
+       hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+       hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+       PRINT_D(vif->ndev, HOSTAPD_DBG,
+               "Radiotap len %d\n", hdr->hdr.it_len);
+       hdr->hdr.it_present = cpu_to_le32
+                       (1 << IEEE80211_RADIOTAP_RATE); /* | */
+       PRINT_D(vif->ndev, HOSTAPD_DBG, "Presentflags %d\n",
+               hdr->hdr.it_present);
+       hdr->rate = 5; /* txrate->bitrate / 5; */
+       skb->dev = vif->ndev;
+       skb_reset_mac_header(skb);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+}
+
+void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size)
+{
+       u32 header, pkt_offset;
+       struct sk_buff *skb = NULL;
+       struct wfi_rtap_hdr *hdr;
+       struct wfi_rtap_cb_hdr *cb_hdr;
+
+       if (!mon_dev)
+               return;
+
+       if (!netif_running(mon_dev)) {
+               PRINT_D(mon_dev, HOSTAPD_DBG,
+                       "Monitor interface already RUNNING\n");
+               return;
+       }
+
+       /* Get WILC header */
+       memcpy(&header, (buff - HOST_HDR_OFFSET), HOST_HDR_OFFSET);
+       le32_to_cpus(&header);
+       /*
+        * The packet offset field contain info about what type of management
+        * the frame we are dealing with and ack status
+        */
+       pkt_offset = GET_PKT_OFFSET(header);
+
+       if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
+               /* hostapd callback mgmt frame */
+
+               skb = dev_alloc_skb(size + sizeof(*cb_hdr));
+               if (!skb) {
+                       PRINT_D(mon_dev, HOSTAPD_DBG,
+                               "Monitor if : No memory to allocate skb");
+                       return;
+               }
+       #if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+               skb_put_data(skb, buff, size);
+
+               cb_hdr = skb_push(skb, sizeof(*cb_hdr));
+       #else
+               memcpy(skb_put(skb, size), buff, size);
+               cb_hdr = (struct wfi_rtap_cb_hdr *)skb_push(skb,
+                                                           sizeof(*cb_hdr));
+       #endif
+               memset(cb_hdr, 0, sizeof(*cb_hdr));
+
+               cb_hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+
+               cb_hdr->hdr.it_len = cpu_to_le16(sizeof(*cb_hdr));
+
+               cb_hdr->hdr.it_present = cpu_to_le32(TX_RADIOTAP_PRESENT);
+
+               cb_hdr->rate = 5;
+
+               if (pkt_offset & IS_MGMT_STATUS_SUCCES) {
+                       /* success */
+                       cb_hdr->tx_flags = IEEE80211_RADIOTAP_F_TX_RTS;
+               } else {
+                       cb_hdr->tx_flags = IEEE80211_RADIOTAP_F_TX_FAIL;
+               }
+
+       } else {
+               skb = dev_alloc_skb(size + sizeof(*hdr));
+
+               if (!skb) {
+                       PRINT_D(mon_dev, HOSTAPD_DBG,
+                               "Monitor if : No memory to allocate skb");
+                       return;
+               }
+       #if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+               skb_put_data(skb, buff, size);
+               hdr = skb_push(skb, sizeof(*hdr));
+       #else
+               memcpy(skb_put(skb, size), buff, size);
+               hdr = (struct wfi_rtap_hdr *)skb_push(skb,
+                                                              sizeof(*hdr));
+       #endif
+               memset(hdr, 0, sizeof(*hdr));
+               hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+               hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+               PRINT_D(mon_dev, HOSTAPD_DBG,
+                       "Radiotap len %d\n", hdr->hdr.it_len);
+               hdr->hdr.it_present = cpu_to_le32
+                               (1 << IEEE80211_RADIOTAP_RATE); /* | */
+               PRINT_D(mon_dev, HOSTAPD_DBG, "Presentflags %d\n",
+                       hdr->hdr.it_present);
+               hdr->rate = 5;
+       }
+
+       skb->dev = mon_dev;
+       skb_reset_mac_header(skb);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+
+       netif_rx(skb);
+}
+
+struct tx_complete_mon_data {
+       int size;
+       void *buff;
+};
+
+static void mgmt_tx_complete(void *priv, int status)
+{
+       struct tx_complete_mon_data *pv_data = priv;
+       u8 *buf =  pv_data->buff;
+
+       if (status == 1) {
+               if (buf[0] == 0x10 || buf[0] == 0xb0)
+                       pr_info("Packet sent Size = %d Add = %p.\n",
+                               pv_data->size, pv_data->buff);
+       } else {
+               pr_info("Couldn't send packet Size = %d Add = %p.\n",
+                       pv_data->size, pv_data->buff);
+       }
+       /*
+        * in case of fully hosting mode, the freeing will be done
+        * in response to the cfg packet
+        */
+       kfree(pv_data->buff);
+
+       kfree(pv_data);
+}
+
+static int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len)
+{
+       struct tx_complete_mon_data *mgmt_tx = NULL;
+
+       if (!dev) {
+               PRINT_ER(dev, "ERROR: dev == NULL\n");
+               return -EFAULT;
+       }
+
+       netif_stop_queue(dev);
+       mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_ATOMIC);
+       if (!mgmt_tx) {
+               PRINT_ER(dev,
+                        "Failed to allocate memory for mgmt_tx structure\n");
+               return -ENOMEM;
+       }
+
+       mgmt_tx->buff = kmemdup(buf, len, GFP_ATOMIC);
+       if (!mgmt_tx->buff) {
+               kfree(mgmt_tx);
+               return -ENOMEM;
+       }
+
+       mgmt_tx->size = len;
+
+       txq_add_mgmt_pkt(dev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size,
+                                  mgmt_tx_complete);
+
+       netif_wake_queue(dev);
+       return 0;
+}
+
+static netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb,
+                                    struct net_device *dev)
+{
+       u32 rtap_len, ret = 0;
+       struct wilc_wfi_mon_priv  *mon_priv;
+       u8 srcadd[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+
+       mon_priv = netdev_priv(dev);
+       if (!mon_priv) {
+               PRINT_ER(dev, "Monitor interface private structure is NULL\n");
+               return -EFAULT;
+       }
+       rtap_len = ieee80211_get_radiotap_len(skb->data);
+       if (skb->len < rtap_len) {
+               PRINT_ER(dev, "Error in radiotap header\n");
+               return -1;
+       }
+
+       skb_pull(skb, rtap_len);
+
+       skb->dev = mon_priv->real_ndev;
+
+       PRINT_D(dev, HOSTAPD_DBG, "Skipping the radiotap header\n");
+       PRINT_D(dev, HOSTAPD_DBG, "SKB netdevice name = %s\n", skb->dev->name);
+       PRINT_D(dev, HOSTAPD_DBG, "MONITOR real dev name = %s\n",
+               mon_priv->real_ndev->name);
+       ether_addr_copy(srcadd, &skb->data[10]);
+       ether_addr_copy(bssid, &skb->data[16]);
+       /*
+        * Identify if data or mgmt packet, if source address and bssid
+        * fields are equal send it to mgmt frames handler
+        */
+       if (!(memcmp(srcadd, bssid, 6))) {
+               ret = mon_mgmt_tx(mon_priv->real_ndev, skb->data, skb->len);
+               if (ret)
+                       PRINT_ER(dev, "fail to mgmt tx\n");
+               dev_kfree_skb(skb);
+       } else {
+               ret = wilc_mac_xmit(skb, mon_priv->real_ndev);
+       }
+
+       return ret;
+}
+
+static const struct net_device_ops wilc_wfi_netdev_ops = {
+       .ndo_start_xmit         = wilc_wfi_mon_xmit,
+
+};
+
+struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
+                                              const char *name,
+                                              struct net_device *real_dev)
+{
+       struct wilc_wfi_mon_priv *priv;
+
+       /*If monitor interface is already initialized, return it*/
+       if (wl->monitor_dev)
+               return wl->monitor_dev;
+
+       wl->monitor_dev = alloc_etherdev(sizeof(struct wilc_wfi_mon_priv));
+       if (!wl->monitor_dev) {
+               PRINT_ER(real_dev, "failed to allocate memory\n");
+               return NULL;
+       }
+       wl->monitor_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+       strncpy(wl->monitor_dev->name, name, IFNAMSIZ);
+       wl->monitor_dev->name[IFNAMSIZ - 1] = 0;
+       wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops;
+
+       if (register_netdevice(wl->monitor_dev)) {
+               PRINT_ER(real_dev, "register_netdevice failed\n");
+               return NULL;
+       }
+       priv = netdev_priv(wl->monitor_dev);
+       if (!priv) {
+               PRINT_ER(real_dev, "private structure is NULL\n");
+               return NULL;
+       }
+
+       priv->real_ndev = real_dev;
+
+       return wl->monitor_dev;
+}
+
+void wilc_wfi_deinit_mon_interface(struct wilc *wl)
+{
+       if (!wl->monitor_dev)
+               return;
+
+       PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG,
+                  "In Deinit monitor interface\n");
+       PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG, "Unregister monitor netdev\n");
+       unregister_netdev(wl->monitor_dev);
+       free_netdev(wl->monitor_dev);
+       PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG,
+                  "Deinit monitor interface done\n");
+       wl->monitor_dev = NULL;
+}
diff --git a/drivers/net/wireless/mchp/wilc_netdev.c b/drivers/net/wireless/mchp/wilc_netdev.c
new file mode 100644 (file)
index 0000000..5f769fa
--- /dev/null
@@ -0,0 +1,1799 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <net/ip.h>
+#include <linux/module.h>
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+#include <linux/inetdevice.h>
+#endif /* DISABLE_PWRSAVE_AND_SCAN_DURING_IP */
+
+#include "wilc_netdev.h"
+#include "wilc_wfi_cfgoperations.h"
+
+#define WILC_MULTICAST_TABLE_SIZE      8
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+bool g_ignore_PS_state;
+#define WILC_IP_TIMEOUT_MS             15000
+
+void handle_pwrsave_for_IP(struct wilc_vif *vif, uint8_t state)
+{
+       struct wilc_priv *priv;
+
+       priv = wdev_priv(vif->ndev->ieee80211_ptr);
+
+       switch (state) {
+       case IP_STATE_OBTAINING:
+
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Obtaining IP, Disable (Scan-Set PowerSave)\n");
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Save the Current state of the PS = %d\n",
+                          vif->pwrsave_current_state);
+
+               vif->obtaining_ip = true;
+
+               /* Set this flag to avoid storing the disabled case of PS which
+                * occurs duringIP
+                */
+               g_ignore_PS_state = true;
+
+               wilc_set_power_mgmt(vif, 0, 0);
+
+               /* Start the DuringIPTimer */
+       #if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+               vif->during_ip_timer.data = (uint32_t)vif;
+       #endif
+               mod_timer(&vif->during_ip_timer,
+                         (jiffies + msecs_to_jiffies(20000)));
+
+               break;
+
+       case IP_STATE_OBTAINED:
+
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "IP obtained , Enable (Scan-Set PowerSave)\n");
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Recover the state of the PS = %d\n",
+                          vif->pwrsave_current_state);
+
+               vif->obtaining_ip = false;
+
+               wilc_set_power_mgmt(vif, vif->pwrsave_current_state, 0);
+
+               del_timer(&vif->during_ip_timer);
+
+               break;
+
+       case IP_STATE_GO_ASSIGNING:
+
+               vif->obtaining_ip = true;
+
+               /* Start the DuringIPTimer */
+       #if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+               vif->during_ip_timer.data = (uint32_t)vif;
+       #endif
+               mod_timer(&vif->during_ip_timer,
+                         (jiffies + msecs_to_jiffies(WILC_IP_TIMEOUT_MS)));
+
+               break;
+
+       default: //IP_STATE_DEFAULT
+
+               vif->obtaining_ip = false;
+
+               /* Stop the DuringIPTimer */
+               del_timer(&vif->during_ip_timer);
+
+               break;
+       }
+}
+
+void store_power_save_current_state(struct wilc_vif *vif, bool val)
+{
+       if (g_ignore_PS_state) {
+               g_ignore_PS_state = false;
+               return;
+       }
+       vif->pwrsave_current_state = val;
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void clear_during_ip(struct timer_list *t)
+#else
+void clear_during_ip(unsigned long arg)
+#endif
+{
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       struct wilc_vif *vif = from_timer(vif, t, during_ip_timer);
+#else
+       struct wilc_vif *vif = (struct wilc_vif *)arg;
+#endif
+
+       PRINT_ER(vif->ndev, "Unable to Obtain IP\n");
+
+       vif->obtaining_ip = false;
+
+       wilc_powersave_state_changes(vif);
+}
+#endif /* DISABLE_PWRSAVE_AND_SCAN_DURING_IP */
+
+static int wilc_mac_open(struct net_device *ndev);
+static int wilc_mac_close(struct net_device *ndev);
+
+int debug_running;
+int recovery_on;
+int wait_for_recovery;
+static int debug_thread(void *arg)
+{
+       struct net_device *dev = arg;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wl;
+       struct wilc_priv *priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
+       signed long timeout;
+       struct host_if_drv *hif_drv = (struct host_if_drv *)priv->hif_drv;
+       int i = 0;
+
+       if (!vif)
+               return -1;
+
+       wl = vif->wilc;
+       if (!wl)
+               return -1;
+
+       complete(&wl->debug_thread_started);
+
+       while (1) {
+               if (!wl->initialized && !kthread_should_stop()) {
+                       msleep(1000);
+                       continue;
+               } else if (!wl->initialized) {
+                       break;
+               }
+
+               if (wait_for_completion_timeout(&wl->debug_thread_started,
+                                               msecs_to_jiffies(6000))) {
+                       while (!kthread_should_stop())
+                               schedule();
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Exit debug thread\n");
+                       return 0;
+               }
+
+               if (!debug_running)
+                       continue;
+               PRINT_INFO(dev, GENERIC_DBG,
+                          "*** Debug Thread Running ***\n");
+               if (cfg_packet_timeout < 5)
+                       continue;
+
+               PRINT_INFO(dev, GENERIC_DBG,
+                          "<Recover>\n");
+               cfg_packet_timeout = 0;
+               timeout = 10;
+               recovery_on = 1;
+               wait_for_recovery = 1;
+               for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++)
+                       wilc_mac_close(wl->vif[i]->ndev);
+               for (i = WILC_NUM_CONCURRENT_IFC; i > 0; i--) {
+                       while (wilc_mac_open(wl->vif[i-1]->ndev) && --timeout)
+                               msleep(100);
+
+                       if (timeout == 0)
+                               PRINT_WRN(vif->ndev, GENERIC_DBG,
+                                         "Couldn't restart ifc %d\n", i);
+               }
+               if (hif_drv->hif_state == HOST_IF_CONNECTED) {
+                       struct wilc_conn_info *conn_info = &hif_drv->conn_info;
+
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "notify the user with the Disconnection\n");
+                       if (hif_drv->usr_scan_req.scan_result) {
+                               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                          "Abort the running OBSS Scan\n");
+                               del_timer(&hif_drv->scan_timer);
+                               handle_scan_done(vif, SCAN_EVENT_ABORTED);
+                       }
+                       if (conn_info->conn_result) {
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+
+                               handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+                               conn_info->conn_result(EVENT_DISCONN_NOTIF,
+                                                      0, conn_info->arg);
+                       } else {
+                               PRINT_ER(vif->ndev, "Connect result NULL\n");
+                       }
+                       eth_zero_addr(hif_drv->assoc_bssid);
+
+                       conn_info->req_ies_len = 0;
+                       kfree(conn_info->req_ies);
+                       conn_info->req_ies = NULL;
+
+                       hif_drv->hif_state = HOST_IF_IDLE;
+               }
+               recovery_on = 0;
+       }
+       return 0;
+}
+
+void wilc_disable_irq(struct wilc *wilc, int wait)
+{
+       if (wait) {
+               PRINT_INFO(wilc->vif[0]->ndev, INT_DBG, "Disabling IRQ ...\n");
+               disable_irq(wilc->dev_irq_num);
+       } else {
+               PRINT_INFO(wilc->vif[0]->ndev, INT_DBG, "Disabling IRQ ...\n");
+               disable_irq_nosync(wilc->dev_irq_num);
+       }
+}
+
+static irqreturn_t host_wakeup_isr(int irq, void *user_data)
+{
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t isr_uh_routine(int irq, void *user_data)
+{
+       struct wilc *wilc = (struct wilc *)user_data;
+       struct net_device *dev = wilc->vif[0]->ndev;
+
+
+       PRINT_INFO(dev, INT_DBG, "Interrupt received UH\n");
+
+       if (wilc->close) {
+               PRINT_ER(dev, "Can't handle UH interrupt\n");
+               return IRQ_HANDLED;
+       }
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t isr_bh_routine(int irq, void *userdata)
+{
+       struct wilc *wilc = (struct wilc *)userdata;
+       struct net_device *dev = wilc->vif[0]->ndev;
+
+
+       if (wilc->close) {
+               PRINT_ER(dev, "Can't handle BH interrupt\n");
+               return IRQ_HANDLED;
+       }
+
+       PRINT_INFO(dev, INT_DBG, "Interrupt received BH\n");
+       wilc_handle_isr(wilc);
+
+       return IRQ_HANDLED;
+}
+
+static int init_irq(struct net_device *dev)
+{
+       int ret = 0;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wl = vif->wilc;
+
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+
+       wl->gpio_irq = gpiod_get(wl->dt_dev, "irq", GPIOD_IN);
+       if (IS_ERR(wl->gpio_irq)) {
+               dev_warn(wl->dev, "failed to get IRQ GPIO, load default\r\n");
+               wl->gpio_irq = gpio_to_desc(GPIO_NUM);
+               if (!wl->gpio_irq) {
+                       dev_warn(wl->dev, "failed to load default irq\r\n");
+                       return -1;
+               }
+       } else {
+               dev_info(wl->dev, "got gpio_irq successfully\r\n");
+       }
+
+       ret = gpiod_direction_input(wl->gpio_irq);
+       if (ret) {
+               PRINT_ER(dev, "could not obtain gpio for WILC_INTR\n");
+               return ret;
+       }
+
+       wl->dev_irq_num = gpiod_to_irq(wl->gpio_irq);
+       if (wl->dev_irq_num < 0) {
+               PRINT_ER(dev, "could not the IRQ\n");
+               goto free_gpio;
+       }
+#else
+       wl->gpio_irq = of_get_named_gpio_flags(wl->dt_dev->of_node,
+                                              "irq-gpios", 0, NULL);
+       if (wl->gpio_irq < 0) {
+               wl->gpio_irq = GPIO_NUM;
+               dev_warn(wl->dev, "failed to get IRQ GPIO, load default\r\n");
+       }
+
+       if ((gpio_request(wl->gpio_irq, "WILC_INTR") == 0) &&
+           (gpio_direction_input(wl->gpio_irq) == 0)) {
+               wl->dev_irq_num = gpio_to_irq(wl->gpio_irq);
+       } else {
+               dev_err(wl->dev, "could not obtain gpio for WILC_INTR\n");
+               wl->gpio_irq = 0;
+               return -1;
+       }
+
+#endif
+
+       if (wl->io_type == WILC_HIF_SPI ||
+               wl->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+               if (request_threaded_irq(wl->dev_irq_num, isr_uh_routine,
+                                        isr_bh_routine, IRQF_TRIGGER_LOW |
+                                                       IRQF_ONESHOT |
+                                                       IRQF_NO_SUSPEND,
+                                        "WILC_IRQ", wl) < 0) {
+                       PRINT_ER(dev, "Failed to request IRQ\n");
+                       goto free_gpio;
+               }
+       } else {
+               if (request_irq(wl->dev_irq_num, host_wakeup_isr,
+                                            IRQF_TRIGGER_FALLING |
+                                            IRQF_NO_SUSPEND,
+                                            "WILC_IRQ", wl) < 0) {
+                       PRINT_ER(dev, "Failed to request IRQ\n");
+                       goto free_gpio;
+               }
+       }
+
+       PRINT_INFO(dev, GENERIC_DBG, "IRQ request succeeded IRQ-NUM= %d\n",
+                  wl->dev_irq_num);
+       enable_irq_wake(wl->dev_irq_num);
+       return ret;
+
+free_gpio:
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+       gpiod_put(wl->gpio_irq);
+       wl->gpio_irq = NULL;
+#else
+       gpio_free(wl->gpio_irq);
+       wl->gpio_irq = 0;
+#endif
+       return -1;
+}
+
+static void deinit_irq(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+
+       /* Deinitialize IRQ */
+       if (wilc->dev_irq_num > 0) {
+               free_irq(wilc->dev_irq_num, wilc);
+               wilc->dev_irq_num = -1;
+       }
+
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+       if (wilc->gpio_irq) {
+               gpiod_put(wilc->gpio_irq);
+               wilc->gpio_irq = NULL;
+       }
+#else
+       if (wilc->gpio_irq > 0) {
+               gpio_free(wilc->gpio_irq);
+               wilc->gpio_irq = 0;
+       }
+
+#endif
+
+}
+
+void wilc_mac_indicate(struct wilc *wilc)
+{
+       s8 status;
+
+       cfg_get_val(wilc, WID_STATUS, &status, 1);
+       if (wilc->mac_status == WILC_MAC_STATUS_INIT) {
+               wilc->mac_status = status;
+               complete(&wilc->sync_event);
+       } else {
+               wilc->mac_status = status;
+       }
+}
+
+void wilc_frmw_to_host(struct wilc_vif *vif, u8 *buff, u32 size,
+                      u32 pkt_offset, u8 status)
+{
+       unsigned int frame_len = 0;
+       int stats;
+       unsigned char *buff_to_send = NULL;
+       struct sk_buff *skb;
+       struct wilc_priv *priv;
+       u8 null_bssid[ETH_ALEN] = {0};
+
+       buff += pkt_offset;
+       priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
+
+       if (size == 0) {
+               PRINT_ER(vif->ndev,
+                        "Discard sending packet with len = %d\n", size);
+               return;
+       }
+
+       frame_len = size;
+       buff_to_send = buff;
+
+       if (status == PKT_STATUS_NEW && buff_to_send[12] == 0x88 &&
+          buff_to_send[13] == 0x8e &&
+          (vif->iftype == WILC_STATION_MODE ||
+           vif->iftype == WILC_CLIENT_MODE) &&
+          ether_addr_equal_unaligned(priv->associated_bss, null_bssid)) {
+               if (!priv->buffered_eap) {
+                       priv->buffered_eap = kmalloc(sizeof(struct
+                                                           wilc_buffered_eap),
+                                                    GFP_ATOMIC);
+                       if (priv->buffered_eap) {
+                               priv->buffered_eap->buff = NULL;
+                               priv->buffered_eap->size = 0;
+                               priv->buffered_eap->pkt_offset = 0;
+                       } else {
+                               PRINT_ER(vif->ndev,
+                                        "failed to alloc buffered_eap\n");
+                               return;
+                       }
+               } else {
+                       kfree(priv->buffered_eap->buff);
+               }
+               priv->buffered_eap->buff = kmalloc(size + pkt_offset,
+                                                  GFP_ATOMIC);
+               priv->buffered_eap->size = size;
+               priv->buffered_eap->pkt_offset = pkt_offset;
+               memcpy(priv->buffered_eap->buff, buff -
+                      pkt_offset, size + pkt_offset);
+       #if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+               priv->eap_buff_timer.data = (unsigned long) priv;
+       #endif
+               mod_timer(&priv->eap_buff_timer, (jiffies +
+                         msecs_to_jiffies(10)));
+               return;
+       }
+       skb = dev_alloc_skb(frame_len);
+       if (!skb) {
+               PRINT_ER(vif->ndev, "Low memory - packet droped\n");
+               return;
+       }
+
+       skb->dev = vif->ndev;
+#if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
+       skb_put_data(skb, buff_to_send, frame_len);
+#else
+       memcpy(skb_put(skb, frame_len), buff_to_send, frame_len);
+#endif
+
+       skb->protocol = eth_type_trans(skb, vif->ndev);
+       vif->netstats.rx_packets++;
+       vif->netstats.rx_bytes += frame_len;
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       stats = netif_rx(skb);
+       PRINT_D(vif->ndev, RX_DBG, "netif_rx ret value: %d\n", stats);
+}
+
+void free_eap_buff_params(void *vp)
+{
+       struct wilc_priv *priv;
+
+       priv = (struct wilc_priv *)vp;
+
+       if (priv->buffered_eap) {
+               kfree(priv->buffered_eap->buff);
+               priv->buffered_eap->buff = NULL;
+
+               kfree(priv->buffered_eap);
+               priv->buffered_eap = NULL;
+       }
+}
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void eap_buff_timeout(struct timer_list *t)
+#else
+void eap_buff_timeout(unsigned long user)
+#endif
+{
+       u8 null_bssid[ETH_ALEN] = {0};
+       u8 *assoc_bss;
+       static u8 timeout = 5;
+       int status = -1;
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       struct wilc_priv *priv = from_timer(priv, t, eap_buff_timer);
+#else
+       struct wilc_priv *priv = (struct wilc_priv *)user;
+#endif
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       assoc_bss = priv->associated_bss;
+       if (!(memcmp(assoc_bss, null_bssid, ETH_ALEN)) && (timeout-- > 0)) {
+               mod_timer(&priv->eap_buff_timer,
+                         (jiffies + msecs_to_jiffies(10)));
+               return;
+       }
+       del_timer(&priv->eap_buff_timer);
+       timeout = 5;
+
+       status = wilc_send_buffered_eap(vif, wilc_frmw_to_host,
+                                       free_eap_buff_params,
+                                       priv->buffered_eap->buff,
+                                       priv->buffered_eap->size,
+                                       priv->buffered_eap->pkt_offset,
+                                       (void *)priv);
+       if (status)
+               PRINT_ER(vif->ndev, "Failed so send buffered eap\n");
+}
+
+void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode)
+{
+       struct wilc_vif *vif = netdev_priv(wilc_netdev);
+       struct wilc *wilc = vif->wilc;
+       u8 i = 0;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "set bssid on[%p]\n", wilc_netdev);
+       for (i = 0; i < wilc->vif_num; i++) {
+               if (wilc_netdev == wilc->vif[i]->ndev) {
+                       if (bssid)
+                               ether_addr_copy(wilc->vif[i]->bssid, bssid);
+                       else
+                               eth_zero_addr(wilc->vif[i]->bssid);
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "set bssid [%pM]\n", wilc->vif[i]->bssid);
+                       wilc->vif[i]->iftype = mode;
+               }
+       }
+}
+
+int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
+{
+       u8 i = 0;
+       u8 ret_val = 0;
+
+       for (i = 0; i < wilc->vif_num; i++)
+               if (!is_zero_ether_addr(wilc->vif[i]->bssid))
+                       ret_val++;
+
+       return ret_val;
+}
+
+struct net_device *wilc_get_if_netdev(struct wilc *wilc, uint8_t ifc)
+{
+       return wilc->vif[ifc]->ndev;
+}
+
+struct host_if_drv *get_drv_hndl_by_ifc(struct wilc *wilc, uint8_t ifc)
+{
+       return wilc->vif[ifc]->hif_drv;
+}
+
+#define TX_BACKOFF_WEIGHT_INCR_STEP (1)
+#define TX_BACKOFF_WEIGHT_DECR_STEP (1)
+#define TX_BACKOFF_WEIGHT_MAX (0)
+#define TX_BACKOFF_WEIGHT_MIN (0)
+#define TX_BCKOFF_WGHT_MS (1)
+
+
+static int wilc_txq_task(void *vp)
+{
+       int ret;
+       u32 txq_count;
+       struct net_device *ndev = vp;
+       int backoff_weight = TX_BACKOFF_WEIGHT_MIN;
+       signed long timeout;
+       struct wilc_vif *vif = netdev_priv(ndev);
+       struct wilc *wl = vif->wilc;
+
+       complete(&wl->txq_thread_started);
+       while (1) {
+               PRINT_INFO(ndev, TX_DBG, "txq_task Taking a nap\n");
+               wait_for_completion(&wl->txq_event);
+               PRINT_INFO(ndev, TX_DBG, "txq_task Who waked me up\n");
+               if (wl->close) {
+                       complete(&wl->txq_thread_started);
+
+                       while (!kthread_should_stop())
+                               schedule();
+                       PRINT_INFO(ndev, TX_DBG, "TX thread stopped\n");
+                       break;
+               }
+               PRINT_INFO(ndev, TX_DBG, "handle the tx packet\n");
+               do {
+                       ret = wilc_wlan_handle_txq(ndev, &txq_count);
+                       if (txq_count < FLOW_CTRL_LOW_THRESHLD) {
+                               PRINT_INFO(ndev, TX_DBG, "Waking up queue\n");
+                               if (wl->vif[0]->mac_opened &&
+                                   netif_queue_stopped(wl->vif[0]->ndev))
+                                       netif_wake_queue(wl->vif[0]->ndev);
+
+                               if (wl->vif[1]->mac_opened &&
+                                   netif_queue_stopped(wl->vif[1]->ndev))
+                                       netif_wake_queue(wl->vif[1]->ndev);
+                       }
+
+                       if (ret == -ENOBUFS) {
+                               timeout = msecs_to_jiffies(TX_BCKOFF_WGHT_MS <<
+                                                          backoff_weight);
+                               do {
+                       /* Back off from sending packets for some time.
+                        * schedule_timeout will allow RX task to run and free
+                        * buffers. Setting state to TASK_INTERRUPTIBLE will
+                        * put the thread back to CPU running queue when it's
+                        * signaled even if 'timeout' isn't elapsed. This gives
+                        * faster chance for reserved SK buffers to be freed
+                        */
+                                       set_current_state(TASK_INTERRUPTIBLE);
+                                       timeout = schedule_timeout(timeout);
+                                       } while (/*timeout*/0);
+                               backoff_weight += TX_BACKOFF_WEIGHT_INCR_STEP;
+                               if (backoff_weight > TX_BACKOFF_WEIGHT_MAX)
+                                       backoff_weight = TX_BACKOFF_WEIGHT_MAX;
+                       } else if (backoff_weight > TX_BACKOFF_WEIGHT_MIN) {
+                               backoff_weight -= TX_BACKOFF_WEIGHT_DECR_STEP;
+                               if (backoff_weight < TX_BACKOFF_WEIGHT_MIN)
+                                       backoff_weight = TX_BACKOFF_WEIGHT_MIN;
+                       }
+               } while (ret == -ENOBUFS && !wl->close);
+       }
+       return 0;
+}
+
+static int wilc_wlan_get_firmware(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       int ret = 0;
+       const struct firmware *wilc_firmware;
+       char *firmware;
+
+
+       if (wilc->chip == WILC_3000) {
+               PRINT_INFO(dev, INIT_DBG, "Detect chip WILC3000\n");
+               firmware = FW_WILC3000_WIFI;
+       } else if (wilc->chip == WILC_1000) {
+               PRINT_INFO(dev, INIT_DBG, "Detect chip WILC1000\n");
+               firmware = FW_WILC1000_WIFi;
+       } else {
+               return -1;
+       }
+
+       PRINT_INFO(dev, INIT_DBG, "loading firmware %s\n", firmware);
+
+       if (!(&vif->ndev->dev)) {
+               PRINT_ER(dev, "Dev  is NULL\n");
+               goto fail;
+       }
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "WLAN firmware: %s\n", firmware);
+       if (request_firmware(&wilc_firmware, firmware, wilc->dev) != 0) {
+               PRINT_ER(dev, "%s - firmware not available\n", firmware);
+               ret = -1;
+               goto fail;
+       }
+       wilc->firmware = wilc_firmware;
+
+fail:
+
+       return ret;
+}
+
+static int wilc_start_firmware(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       int ret = 0;
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "Starting Firmware ...\n");
+
+       ret = wilc_wlan_start(wilc);
+       if (ret < 0) {
+               PRINT_ER(dev, "Failed to start Firmware\n");
+               return ret;
+       }
+       PRINT_INFO(vif->ndev, INIT_DBG, "Waiting for FW to get ready ...\n");
+
+       if (!wait_for_completion_timeout(&wilc->sync_event,
+                                        msecs_to_jiffies(500))) {
+               PRINT_INFO(vif->ndev, INIT_DBG, "Firmware start timed out\n");
+               return -ETIME;
+       }
+       PRINT_INFO(vif->ndev, INIT_DBG, "Firmware successfully started\n");
+
+       return 0;
+}
+
+static int wilc_firmware_download(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       int ret = 0;
+
+       if (!wilc->firmware) {
+               PRINT_ER(dev, "Firmware buffer is NULL\n");
+               ret = -ENOBUFS;
+       }
+       PRINT_INFO(vif->ndev, INIT_DBG, "Downloading Firmware ...\n");
+       ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data,
+                                         wilc->firmware->size);
+       if (ret < 0)
+               goto fail;
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "Download Succeeded\n");
+
+fail:
+       release_firmware(wilc->firmware);
+       wilc->firmware = NULL;
+
+       return ret;
+}
+
+static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif)
+{
+       struct wilc_priv *priv;
+       struct host_if_drv *hif_drv;
+       u8 b;
+       u16 hw;
+       u32 w;
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "Start configuring Firmware\n");
+       priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+       hif_drv = (struct host_if_drv *)priv->hif_drv;
+       PRINT_D(vif->ndev, INIT_DBG, "Host = %p\n", hif_drv);
+
+       w = vif->iftype;
+       cpu_to_le32s(&w);
+       if (!cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4, 0, 0))
+               goto fail;
+
+       b = WILC_FW_BSS_TYPE_INFRA;
+       if (!cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_TX_RATE_AUTO;
+       if (!cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_OPER_MODE_G_MIXED_11B_2;
+       if (!cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = WILC_FW_PREAMBLE_AUTO;
+       if (!cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_11N_PROT_AUTO;
+       if (!cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_ACTIVE_SCAN;
+       if (!cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_SITE_SURVEY_OFF;
+       if (!cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0))
+               goto fail;
+
+       hw = 0xffff;
+       cpu_to_le16s(&hw);
+       if (!cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0))
+               goto fail;
+
+       hw = 2346;
+       cpu_to_le16s(&hw);
+       if (!cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0))
+               goto fail;
+
+       b = 0;
+       if (!cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0))
+               goto fail;
+
+       b = 1;
+       if (!cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_NO_POWERSAVE;
+       if (!cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_SEC_NO;
+       if (!cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_AUTH_OPEN_SYSTEM;
+       if (!cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0))
+               goto fail;
+
+       b = 3;
+       if (!cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0))
+               goto fail;
+
+       b = 3;
+       if (!cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_ACK_POLICY_NORMAL;
+       if (!cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0))
+               goto fail;
+
+       b = 0;
+       if (!cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1,
+                              0, 0))
+               goto fail;
+
+       b = 48;
+       if (!cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = 28;
+       if (!cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0,
+                              0))
+               goto fail;
+
+       hw = 100;
+       cpu_to_le16s(&hw);
+       if (!cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0))
+               goto fail;
+
+       b = WILC_FW_REKEY_POLICY_DISABLE;
+       if (!cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0))
+               goto fail;
+
+       w = 84600;
+       cpu_to_le32s(&w);
+       if (!cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0))
+               goto fail;
+
+       w = 500;
+       cpu_to_le32s(&w);
+       if (!cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0,
+                              0))
+               goto fail;
+
+       b = 1;
+       if (!cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = WILC_FW_ERP_PROT_SELF_CTS;
+       if (!cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0))
+               goto fail;
+
+       b = 1;
+       if (!cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0))
+               goto fail;
+
+       b = WILC_FW_11N_OP_MODE_HT_MIXED;
+       if (!cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = 1;
+       if (!cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT;
+       if (!cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1,
+                              0, 0))
+               goto fail;
+
+       b = WILC_FW_HT_PROT_RTS_CTS_NONHT;
+       if (!cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0))
+               goto fail;
+
+       b = 0;
+       if (!cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = 7;
+       if (!cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0,
+                              0))
+               goto fail;
+
+       b = 1;
+       if (!cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1,
+                              1, 0))
+               goto fail;
+
+       return 0;
+
+fail:
+       return -1;
+}
+
+static void wlan_deinit_locks(struct wilc *wilc)
+{
+       pr_info("De-Initializing Locks\n");
+
+       mutex_destroy(&wilc->hif_cs);
+       mutex_destroy(&wilc->rxq_cs);
+       mutex_destroy(&wilc->cfg_cmd_lock);
+       mutex_destroy(&wilc->txq_add_to_head_cs);
+       mutex_destroy(&wilc->cs);
+}
+
+static void wlan_deinitialize_threads(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wl = vif->wilc;
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n");
+       if (!recovery_on) {
+               PRINT_INFO(vif->ndev, INIT_DBG, "Deinit debug Thread\n");
+               debug_running = false;
+               if (&wl->debug_thread_started)
+                       complete(&wl->debug_thread_started);
+               if (wl->debug_thread) {
+                       kthread_stop(wl->debug_thread);
+                       wl->debug_thread = NULL;
+               }
+       }
+
+       wl->close = 1;
+       PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n");
+
+       complete(&wl->txq_event);
+
+       if (wl->txq_thread) {
+               kthread_stop(wl->txq_thread);
+               wl->txq_thread = NULL;
+       }
+}
+
+static void wilc_wlan_deinitialize(struct net_device *dev)
+{
+       int ret;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wl = vif->wilc;
+
+       if (wl->initialized) {
+               PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing wilc  ...\n");
+
+               if (!wl) {
+                       PRINT_ER(dev, "wl is NULL\n");
+                       return;
+               }
+
+               PRINT_D(vif->ndev, INIT_DBG, "destroy aging timer\n");
+
+               PRINT_INFO(vif->ndev, INIT_DBG, "Disabling IRQ\n");
+               if (wl->io_type == WILC_HIF_SPI ||
+                       wl->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+                       wilc_disable_irq(wl, 1);
+               } else {
+                       if (wl->hif_func->disable_interrupt) {
+                               mutex_lock(&wl->hif_cs);
+                               wl->hif_func->disable_interrupt(wl);
+                               mutex_unlock(&wl->hif_cs);
+                       }
+               }
+               complete(&wl->txq_event);
+
+               PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n");
+               wlan_deinitialize_threads(dev);
+               PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing IRQ\n");
+               deinit_irq(dev);
+
+               ret = wilc_wlan_stop(wl, vif);
+               if (ret == 0)
+                       PRINT_ER(dev, "failed in wlan_stop\n");
+
+               PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing WILC Wlan\n");
+               wilc_wlan_cleanup(dev);
+
+               wl->initialized = false;
+
+               PRINT_INFO(dev, INIT_DBG, "wilc deinitialization Done\n");
+       } else {
+               PRINT_INFO(dev, INIT_DBG, "wilc is not initialized\n");
+       }
+}
+
+static void wlan_init_locks(struct wilc *wl)
+{
+       pr_info("Initializing Locks ...\n");
+
+       mutex_init(&wl->rxq_cs);
+       mutex_init(&wl->cfg_cmd_lock);
+
+       spin_lock_init(&wl->txq_spinlock);
+       mutex_init(&wl->txq_add_to_head_cs);
+       mutex_init(&wl->hif_cs);
+       mutex_init(&wl->cs);
+
+       init_completion(&wl->txq_event);
+
+       init_completion(&wl->cfg_event);
+       init_completion(&wl->sync_event);
+       init_completion(&wl->txq_thread_started);
+       init_completion(&wl->debug_thread_started);
+}
+
+static int wlan_initialize_threads(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "Initializing Threads ...\n");
+       PRINT_INFO(vif->ndev, INIT_DBG, "Creating kthread for transmission\n");
+       wilc->txq_thread = kthread_run(wilc_txq_task, (void *)dev,
+                                      "K_TXQ_TASK");
+       if (IS_ERR(wilc->txq_thread)) {
+               PRINT_ER(dev, "couldn't create TXQ thread\n");
+               wilc->close = 1;
+               return PTR_ERR(wilc->txq_thread);
+       }
+       wait_for_completion(&wilc->txq_thread_started);
+
+       if (!debug_running) {
+               PRINT_INFO(vif->ndev, INIT_DBG,
+                          "Creating kthread for Debugging\n");
+               wilc->debug_thread = kthread_run(debug_thread, (void *)dev,
+                                                "WILC_DEBUG");
+               if (IS_ERR(wilc->debug_thread)) {
+                       PRINT_ER(dev, "couldn't create debug thread\n");
+                       wilc->close = 1;
+                       kthread_stop(wilc->txq_thread);
+                       return PTR_ERR(wilc->debug_thread);
+               }
+               debug_running = true;
+               wait_for_completion(&wilc->debug_thread_started);
+       }
+
+       return 0;
+}
+
+static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif)
+{
+       int ret = 0;
+       struct wilc *wl = vif->wilc;
+
+       if (!wl->initialized) {
+               wl->mac_status = WILC_MAC_STATUS_INIT;
+               wl->close = 0;
+               wl->initialized = 0;
+
+               ret = wilc_wlan_init(dev);
+               if (ret < 0) {
+                       PRINT_ER(dev, "Initializing WILC_Wlan FAILED\n");
+                       ret = -EIO;
+                       goto fail;
+               }
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "WILC Initialization done\n");
+               if (init_irq(dev)) {
+                       ret = -EIO;
+                       goto fail;
+               }
+
+               ret = wlan_initialize_threads(dev);
+               if (ret < 0) {
+                       PRINT_ER(dev, "Initializing Threads FAILED\n");
+                       ret = -EIO;
+                       goto fail_wilc_wlan;
+               }
+
+               if (wl->io_type == WILC_HIF_SDIO &&
+                   wl->hif_func->enable_interrupt(wl)) {
+                       PRINT_ER(dev, "couldn't initialize IRQ\n");
+                       ret = -EIO;
+                       goto fail_irq_init;
+               }
+
+               if (wilc_wlan_get_firmware(dev)) {
+                       PRINT_ER(dev, "Can't get firmware\n");
+                       ret = -EIO;
+                       goto fail_irq_enable;
+               }
+
+               ret = wilc_firmware_download(dev);
+               if (ret < 0) {
+                       PRINT_ER(dev, "Failed to download firmware\n");
+                       ret = -EIO;
+                       goto fail_irq_enable;
+               }
+
+               ret = wilc_start_firmware(dev);
+               if (ret < 0) {
+                       PRINT_ER(dev, "Failed to start firmware\n");
+                       ret = -EIO;
+                       goto fail_irq_enable;
+               }
+
+               if (cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) {
+                       int size;
+                       char firmware_ver[50];
+
+                       size = cfg_get_val(wl, WID_FIRMWARE_VERSION,
+                                                    firmware_ver,
+                                                    sizeof(firmware_ver));
+                       firmware_ver[size] = '\0';
+                       PRINT_INFO(dev, INIT_DBG, "WILC Firmware Ver = %s\n",
+                                  firmware_ver);
+               }
+
+               ret = wilc_init_fw_config(dev, vif);
+               if (ret < 0) {
+                       PRINT_ER(dev, "Failed to configure firmware\n");
+                       ret = -EIO;
+                       goto fail_fw_start;
+               }
+
+               wl->initialized = true;
+               return 0;
+
+fail_fw_start:
+               wilc_wlan_stop(wl, vif);
+
+fail_irq_enable:
+               if (wl->io_type == WILC_HIF_SDIO)
+                       wl->hif_func->disable_interrupt(wl);
+fail_irq_init:
+               deinit_irq(dev);
+
+               wlan_deinitialize_threads(dev);
+fail_wilc_wlan:
+               wilc_wlan_cleanup(dev);
+fail:
+               PRINT_ER(dev, "WLAN initialization FAILED\n");
+       } else {
+               PRINT_WRN(vif->ndev, INIT_DBG, "wilc already initialized\n");
+       }
+       return ret;
+}
+
+static int mac_init_fn(struct net_device *ndev)
+{
+       netif_start_queue(ndev);
+       netif_stop_queue(ndev);
+
+       return 0;
+}
+
+static int wilc_mac_open(struct net_device *ndev)
+{
+       struct wilc_vif *vif = netdev_priv(ndev);
+       struct wilc *wl = vif->wilc;
+       struct wilc_priv *priv = wdev_priv(vif->ndev->ieee80211_ptr);
+       unsigned char mac_add[ETH_ALEN] = {0};
+       int ret = 0;
+
+       if (!wl || !wl->dev) {
+               PRINT_ER(ndev, "device not ready\n");
+               return -ENODEV;
+       }
+
+       PRINT_INFO(ndev, INIT_DBG, "MAC OPEN[%p] %s\n", ndev, ndev->name);
+
+       if (wl->open_ifcs == 0)
+               wilc_bt_power_up(wl, DEV_WIFI);
+
+       if (!recovery_on) {
+               ret = wilc_init_host_int(ndev);
+               if (ret < 0) {
+                       PRINT_ER(ndev, "Failed to initialize host interface\n");
+                       return ret;
+               }
+       }
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "*** re-init ***\n");
+       ret = wilc_wlan_initialize(ndev, vif);
+       if (ret < 0) {
+               PRINT_ER(ndev, "Failed to initialize wilc\n");
+               if (!recovery_on)
+                       wilc_deinit_host_int(ndev);
+               return ret;
+       }
+
+       wait_for_recovery = 0;
+       if (!(memcmp(ndev->name, IFC_0, 5))) {
+               vif->ifc_id = WILC_WLAN_IFC;
+       } else if (!(memcmp(ndev->name, IFC_1, 4))) {
+               vif->ifc_id = WILC_P2P_IFC;
+       } else {
+               PRINT_ER(vif->ndev, "Unknown interface name\n");
+               wilc_deinit_host_int(ndev);
+               wilc_wlan_deinitialize(ndev);
+               return -ENODEV;
+       }
+       wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+                                vif->iftype, vif->ifc_id);
+       wilc_set_operation_mode(vif, vif->iftype);
+       wilc_get_mac_address(vif, mac_add);
+       PRINT_INFO(vif->ndev, INIT_DBG, "Mac address: %pM\n", mac_add);
+
+       if (!is_valid_ether_addr(mac_add)) {
+               PRINT_ER(ndev, "Wrong MAC address\n");
+               wilc_deinit_host_int(ndev);
+               wilc_wlan_deinitialize(ndev);
+               return -EINVAL;
+       }
+       ether_addr_copy(ndev->dev_addr, mac_add);
+
+       wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy,
+                                vif->ndev->ieee80211_ptr,
+                                vif->frame_reg[0].type,
+                                vif->frame_reg[0].reg);
+       wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy,
+                                vif->ndev->ieee80211_ptr,
+                                vif->frame_reg[1].type,
+                                vif->frame_reg[1].reg);
+       netif_wake_queue(ndev);
+       wl->open_ifcs++;
+       priv->p2p.local_random = 0x01;
+       vif->mac_opened = 1;
+       return 0;
+}
+
+static struct net_device_stats *mac_stats(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+
+       return &vif->netstats;
+}
+
+static int wilc_set_mac_addr(struct net_device *dev, void *p)
+{
+       int result;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct sockaddr *addr = (struct sockaddr *)p;
+       struct wilc *wilc = vif->wilc;
+       unsigned char mac_addr[6] = {0};
+       int i;
+
+       if (!is_valid_ether_addr(addr->sa_data)) {
+               PRINT_INFO(vif->ndev, INIT_DBG, "Invalid MAC address\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < wilc->vif_num; i++) {
+               wilc_get_mac_address(wilc->vif[i], mac_addr);
+               if (ether_addr_equal(addr->sa_data, mac_addr)) {
+                       if (vif != wilc->vif[i]) {
+                               PRINT_INFO(vif->ndev, INIT_DBG,
+                                          "MAC address is alredy in use\n");
+                               return -EINVAL;
+                       } else {
+                               return 0;
+                       }
+               }
+       }
+
+       /* configure new MAC address */
+       result = wilc_set_mac_address(vif, (u8 *)addr->sa_data);
+       ether_addr_copy(vif->bssid, addr->sa_data);
+       ether_addr_copy(vif->ndev->dev_addr, vif->bssid);
+
+       return result;
+}
+
+static void wilc_set_multicast_list(struct net_device *dev)
+{
+       struct netdev_hw_addr *ha;
+       struct wilc_vif *vif = netdev_priv(dev);
+       int i;
+       u8 *mc_list;
+       u8 *cur_mc;
+
+       PRINT_INFO(vif->ndev, INIT_DBG,
+                  "Setting mcast List with count = %d.\n", dev->mc.count);
+       if (dev->flags & IFF_PROMISC) {
+               PRINT_INFO(vif->ndev, INIT_DBG,
+                          "Set promiscuous mode ON retrive all pkts\n");
+               return;
+       }
+
+       if (dev->flags & IFF_ALLMULTI ||
+           dev->mc.count > WILC_MULTICAST_TABLE_SIZE) {
+               PRINT_INFO(vif->ndev, INIT_DBG,
+                          "Disable mcast filter retrive multicast pkts\n");
+               wilc_setup_multicast_filter(vif, 0, 0, NULL);
+               return;
+       }
+
+       if (dev->mc.count == 0) {
+               PRINT_INFO(vif->ndev, INIT_DBG,
+                          "Enable mcast filter retrive directed pkts only\n");
+               wilc_setup_multicast_filter(vif, 1, 0, NULL);
+               return;
+       }
+
+       mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC);
+       if (!mc_list)
+               return;
+
+       cur_mc = mc_list;
+       i = 0;
+       netdev_for_each_mc_addr(ha, dev) {
+               memcpy(cur_mc, ha->addr, ETH_ALEN);
+               PRINT_INFO(vif->ndev, INIT_DBG, "Entry[%d]: %pM\n", i, cur_mc);
+               i++;
+               cur_mc += ETH_ALEN;
+       }
+
+       if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list))
+               kfree(mc_list);
+}
+
+static void wilc_tx_complete(void *priv, int status)
+{
+       struct tx_complete_data *pv_data = priv;
+
+       if (status == 1)
+               PRINT_INFO(pv_data->vif->ndev, TX_DBG,
+                         "Packet sentSize= %d Add= %p SKB= %p\n",
+                         pv_data->size, pv_data->buff, pv_data->skb);
+       else
+               PRINT_INFO(pv_data->vif->ndev, TX_DBG,
+                          "Couldn't send pkt Size= %d Add= %p SKB= %p\n",
+                          pv_data->size, pv_data->buff, pv_data->skb);
+       dev_kfree_skb(pv_data->skb);
+       kfree(pv_data);
+}
+
+netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct wilc_vif *vif = netdev_priv(ndev);
+       struct wilc *wilc = vif->wilc;
+       struct tx_complete_data *tx_data = NULL;
+       int queue_count;
+
+       PRINT_INFO(vif->ndev, TX_DBG,
+                  "Sending packet just received from TCP/IP\n");
+       if (skb->dev != ndev) {
+               PRINT_ER(ndev, "Packet not destined to this device\n");
+               return NETDEV_TX_OK;
+       }
+
+       tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC);
+       if (!tx_data) {
+               PRINT_ER(ndev, "Failed to alloc memory for tx_data struct\n");
+               dev_kfree_skb(skb);
+               netif_wake_queue(ndev);
+               return NETDEV_TX_OK;
+       }
+
+       tx_data->buff = skb->data;
+       tx_data->size = skb->len;
+       tx_data->skb  = skb;
+
+       PRINT_D(vif->ndev, TX_DBG, "Sending pkt Size= %d Add= %p SKB= %p\n",
+               tx_data->size, tx_data->buff, tx_data->skb);
+       PRINT_D(vif->ndev, TX_DBG, "Adding tx pkt to TX Queue\n");
+       vif->netstats.tx_packets++;
+       vif->netstats.tx_bytes += tx_data->size;
+       tx_data->bssid = wilc->vif[vif->idx]->bssid;
+       tx_data->vif = vif;
+       queue_count = txq_add_net_pkt(ndev, (void *)tx_data,
+                                     tx_data->buff, tx_data->size,
+                                     wilc_tx_complete);
+
+       if (queue_count > FLOW_CTRL_UP_THRESHLD) {
+               if (wilc->vif[0]->mac_opened)
+                       netif_stop_queue(wilc->vif[0]->ndev);
+               if (wilc->vif[1]->mac_opened)
+                       netif_stop_queue(wilc->vif[1]->ndev);
+       }
+
+       return NETDEV_TX_OK;
+}
+
+static int wilc_mac_close(struct net_device *ndev)
+{
+       struct wilc_vif *vif = netdev_priv(ndev);
+       struct wilc *wl = vif->wilc;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "Mac close\n");
+
+       if (wl->open_ifcs > 0) {
+               wl->open_ifcs--;
+       } else {
+               PRINT_ER(ndev, "MAC close called with no opened interfaces\n");
+               return 0;
+       }
+
+       if (vif->ndev) {
+               netif_stop_queue(vif->ndev);
+
+       if (!recovery_on)
+               wilc_deinit_host_int(vif->ndev);
+       }
+
+       if (wl->open_ifcs == 0) {
+               PRINT_INFO(ndev, GENERIC_DBG, "Deinitializing wilc\n");
+               wl->close = 1;
+               wilc_wlan_deinitialize(ndev);
+       }
+
+       vif->mac_opened = 0;
+
+       return 0;
+}
+
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
+{
+       int i = 0;
+       struct wilc_vif *vif;
+
+       for (i = 0; i < wilc->vif_num; i++) {
+               u16 type;
+
+               vif = netdev_priv(wilc->vif[i]->ndev);
+               if (vif->monitor_flag) {
+                       wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size);
+                       return;
+               }
+               type = le16_to_cpup((__le16 *)buff);
+               if ((type == vif->frame_reg[0].type && vif->frame_reg[0].reg) ||
+                   (type == vif->frame_reg[1].type && vif->frame_reg[1].reg))
+                       wilc_wfi_p2p_rx(vif->ndev, buff, size);
+       }
+}
+
+static const struct net_device_ops wilc_netdev_ops = {
+       .ndo_init = mac_init_fn,
+       .ndo_open = wilc_mac_open,
+       .ndo_stop = wilc_mac_close,
+       .ndo_set_mac_address = wilc_set_mac_addr,
+       .ndo_start_xmit = wilc_mac_xmit,
+       .ndo_get_stats = mac_stats,
+       .ndo_set_rx_mode  = wilc_set_multicast_list,
+};
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+static int dev_state_ev_handler(struct notifier_block *this,
+                               unsigned long event, void *ptr)
+{
+       struct in_ifaddr *dev_iface = ptr;
+       struct wilc_priv *priv;
+       struct host_if_drv *hif_drv;
+       struct net_device *dev;
+       struct wilc_vif *vif;
+
+       if (!dev_iface || !dev_iface->ifa_dev || !dev_iface->ifa_dev->dev) {
+               pr_err("dev_iface = NULL\n");
+               return NOTIFY_DONE;
+       }
+
+       dev  = (struct net_device *)dev_iface->ifa_dev->dev;
+       if (dev->netdev_ops != &wilc_netdev_ops) {
+               pr_info("interface is not ours\n");
+               return NOTIFY_DONE;
+       }
+
+       if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) {
+               pr_err("No Wireless registerd\n");
+               return NOTIFY_DONE;
+       }
+
+       priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+       if (!priv) {
+               pr_err("No Wireless Priv\n");
+               return NOTIFY_DONE;
+       }
+       vif = netdev_priv(dev);
+       hif_drv = (struct host_if_drv *)priv->hif_drv;
+       if (!vif || !hif_drv) {
+               PRINT_WRN(vif->ndev, GENERIC_DBG, "No Wireless Priv\n");
+               return NOTIFY_DONE;
+       }
+
+       switch (event) {
+       case NETDEV_UP:
+               PRINT_INFO(vif->ndev, GENERIC_DBG, "event NETDEV_UP%p\n", dev);
+               PRINT_D(vif->ndev, GENERIC_DBG,
+                       "\n =========== IP Address Obtained ============\n\n");
+               if (vif->iftype == WILC_STATION_MODE ||
+                   vif->iftype == WILC_CLIENT_MODE) {
+                       hif_drv->ifc_up = 1;
+
+                       handle_pwrsave_for_IP(vif, IP_STATE_OBTAINED);
+               }
+               break;
+
+       case NETDEV_DOWN:
+               PRINT_INFO(vif->ndev, GENERIC_DBG, "event=NETDEV_DOWN %p\n",
+                          dev);
+               if (vif->iftype == WILC_STATION_MODE ||
+                   vif->iftype == WILC_CLIENT_MODE) {
+                       hif_drv->ifc_up = 0;
+                       handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+               }
+               break;
+
+       default:
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "[%s] unknown dev event %lu\n",
+                          dev_iface->ifa_label, event);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+static struct notifier_block g_dev_notifier = {
+       .notifier_call = dev_state_ev_handler
+};
+#endif
+
+void wilc_netdev_cleanup(struct wilc *wilc)
+{
+       int i;
+
+       if (!wilc)
+               return;
+
+       if (wilc->firmware) {
+               release_firmware(wilc->firmware);
+               wilc->firmware = NULL;
+       }
+
+       for (i = WILC_NUM_CONCURRENT_IFC - 1 ; i >= 0; i--)
+               if (wilc->vif[i] && wilc->vif[i]->ndev) {
+                       PRINT_INFO(wilc->vif[i]->ndev, INIT_DBG,
+                                  "Unregistering netdev %p\n",
+                                  wilc->vif[i]->ndev);
+                       unregister_netdev(wilc->vif[i]->ndev);
+                       PRINT_INFO(wilc->vif[i]->ndev, INIT_DBG,
+                                  "Freeing Wiphy...\n");
+                       wilc_free_wiphy(wilc->vif[i]->ndev);
+                       PRINT_INFO(wilc->vif[i]->ndev, INIT_DBG,
+                                  "Freeing netdev...\n");
+                       free_netdev(wilc->vif[i]->ndev);
+               }
+
+       wilc_wfi_deinit_mon_interface(wilc);
+       #ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+               unregister_inetaddr_notifier(&g_dev_notifier);
+       #endif
+
+       flush_workqueue(wilc->hif_workqueue);
+       destroy_workqueue(wilc->hif_workqueue);
+       wilc->hif_workqueue = NULL;
+       cfg_deinit(wilc);
+#ifdef WILC_DEBUGFS
+       wilc_debugfs_remove();
+#endif
+       wilc_sysfs_exit();
+       wlan_deinit_locks(wilc);
+       kfree(wilc->bus_data);
+       kfree(wilc);
+       pr_info("Module_exit Done.\n");
+}
+
+int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type,
+                    const struct wilc_hif_func *ops)
+{
+       int i, ret;
+       struct wilc_vif *vif;
+       struct net_device *ndev;
+       struct wilc *wl;
+       struct wireless_dev *wdev;
+
+       wl = kzalloc(sizeof(*wl), GFP_KERNEL);
+       if (!wl)
+               return -ENOMEM;
+
+       *wilc = wl;
+
+       wlan_init_locks(wl);
+
+       ret = cfg_init(wl);
+       if (ret)
+               goto free_locks;
+
+#ifdef WILC_DEBUGFS
+       if (wilc_debugfs_init()) {
+               ret = -ENOMEM;
+               goto free_cfg;
+       }
+#endif
+       wl->io_type = io_type;
+       wl->hif_func = ops;
+
+       for (i = 0; i < NQUEUES; i++)
+               INIT_LIST_HEAD(&wl->txq[i].txq_head.list);
+
+       INIT_LIST_HEAD(&wl->rxq_head.list);
+
+       wl->hif_workqueue = create_singlethread_workqueue("WILC_wq");
+       if (!wl->hif_workqueue) {
+               ret = -ENOMEM;
+               goto free_debug_fs;
+       }
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       register_inetaddr_notifier(&g_dev_notifier);
+#endif
+
+       for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) {
+               ndev = alloc_etherdev(sizeof(struct wilc_vif));
+               if (!ndev) {
+                       ret = -ENOMEM;
+                       goto free_ndev;
+               }
+
+               vif = netdev_priv(ndev);
+
+               if (i == 0)
+                       strcpy(ndev->name, "wlan%d");
+               else
+                       strcpy(ndev->name, "p2p%d");
+
+               vif->idx = wl->vif_num;
+               vif->wilc = *wilc;
+               vif->ndev = ndev;
+               wl->vif[i] = vif;
+               wl->vif_num = i + 1;
+
+               ndev->netdev_ops = &wilc_netdev_ops;
+
+               wdev = wilc_create_wiphy(ndev, dev);
+               if (!wdev) {
+                       PRINT_ER(ndev, "Can't register WILC Wiphy\n");
+                       ret = -ENOMEM;
+                       goto free_ndev;
+               }
+
+               SET_NETDEV_DEV(ndev, dev);
+
+               vif->ndev->ieee80211_ptr = wdev;
+               vif->ndev->ml_priv = vif;
+               wdev->netdev = vif->ndev;
+               vif->netstats.rx_packets = 0;
+               vif->netstats.tx_packets = 0;
+               vif->netstats.rx_bytes = 0;
+               vif->netstats.tx_bytes = 0;
+
+               ret = register_netdev(ndev);
+               if (ret) {
+                       PRINT_ER(ndev, "Device couldn't be registered - %s\n",
+                              ndev->name);
+                       goto free_ndev;
+               }
+
+               vif->iftype = WILC_STATION_MODE;
+               vif->mac_opened = 0;
+       }
+       wilc_sysfs_init(wl->vif[0], wl->vif[1]);
+
+       return 0;
+free_ndev:
+       for (; i >= 0; i--) {
+               if (wl->vif[i]) {
+                       if (wl->vif[i]->iftype == WILC_STATION_MODE)
+                               unregister_netdev(wl->vif[i]->ndev);
+
+                       if (wl->vif[i]->ndev) {
+                               wilc_free_wiphy(wl->vif[i]->ndev);
+                               free_netdev(wl->vif[i]->ndev);
+                       }
+               }
+       }
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       unregister_inetaddr_notifier(&g_dev_notifier);
+#endif
+       destroy_workqueue(wl->hif_workqueue);
+
+free_debug_fs:
+#ifdef WILC_DEBUGFS
+       wilc_debugfs_remove();
+free_cfg:
+#endif
+       cfg_deinit(wl);
+free_locks:
+       wlan_deinit_locks(wl);
+       kfree(wl);
+       return ret;
+}
+
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+static void wilc_wlan_power(struct wilc *wilc, int power)
+{
+       struct gpio_desc *gpio_reset;
+       struct gpio_desc *gpio_chip_en;
+
+       pr_info("wifi_pm : %d\n", power);
+
+       gpio_reset = gpiod_get(wilc->dt_dev, "reset", GPIOD_ASIS);
+       if (IS_ERR(gpio_reset)) {
+               dev_warn(wilc->dev, "failed to get Reset GPIO, try default\r\n");
+               gpio_reset = gpio_to_desc(GPIO_NUM_RESET);
+               if (!gpio_reset) {
+                       dev_warn(wilc->dev,
+                                "failed to get default Reset GPIO\r\n");
+                       return;
+               }
+       } else {
+               dev_info(wilc->dev, "succesfully got gpio_reset\r\n");
+       }
+
+       gpio_chip_en = gpiod_get(wilc->dt_dev, "chip_en", GPIOD_ASIS);
+       if (IS_ERR(gpio_chip_en)) {
+               gpio_chip_en = gpio_to_desc(GPIO_NUM_CHIP_EN);
+               if (!gpio_chip_en) {
+                       dev_warn(wilc->dev,
+                                "failed to get default chip_en GPIO\r\n");
+                       gpiod_put(gpio_reset);
+                       return;
+               }
+       } else {
+               dev_info(wilc->dev, "succesfully got gpio_chip_en\r\n");
+       }
+
+       if (power) {
+               gpiod_direction_output(gpio_chip_en, 1);
+               mdelay(5);
+               gpiod_direction_output(gpio_reset, 1);
+       } else {
+               gpiod_direction_output(gpio_reset, 0);
+               gpiod_direction_output(gpio_chip_en, 0);
+       }
+       gpiod_put(gpio_chip_en);
+       gpiod_put(gpio_reset);
+}
+#else
+static void wilc_wlan_power(struct wilc *wilc, int power)
+{
+       int gpio_reset;
+       int gpio_chip_en;
+       struct device_node *of_node = wilc->dt_dev->of_node;
+
+       pr_info("wifi_pm : %d\n", power);
+
+       gpio_reset = of_get_named_gpio_flags(of_node, "reset-gpios", 0, NULL);
+
+       if (gpio_reset < 0) {
+               gpio_reset = GPIO_NUM_RESET;
+               pr_info("wifi_pm : load default reset GPIO %d\n", gpio_reset);
+       }
+
+       gpio_chip_en = of_get_named_gpio_flags(of_node, "chip_en-gpios", 0,
+                                              NULL);
+
+       if (gpio_chip_en < 0) {
+               gpio_chip_en = GPIO_NUM_CHIP_EN;
+               pr_info("wifi_pm : load default chip_en GPIO %d\n",
+                       gpio_chip_en);
+       }
+
+       if (gpio_request(gpio_chip_en, "CHIP_EN") == 0 &&
+           gpio_request(gpio_reset, "RESET") == 0) {
+               gpio_direction_output(gpio_chip_en, 0);
+               gpio_direction_output(gpio_reset, 0);
+               if (power) {
+                       gpio_set_value(gpio_chip_en, 1);
+                       mdelay(5);
+                       gpio_set_value(gpio_reset, 1);
+               } else {
+                       gpio_set_value(gpio_reset, 0);
+                       gpio_set_value(gpio_chip_en, 0);
+               }
+               gpio_free(gpio_chip_en);
+               gpio_free(gpio_reset);
+       } else {
+               dev_err(wilc->dev,
+                       "Error requesting GPIOs for CHIP_EN and RESET");
+       }
+
+}
+#endif
+
+void wilc_wlan_power_on_sequence(struct wilc *wilc)
+{
+       wilc_wlan_power(wilc, 0);
+       wilc_wlan_power(wilc, 1);
+}
+
+void wilc_wlan_power_off_sequence(struct wilc *wilc)
+{
+       wilc_wlan_power(wilc, 0);
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/mchp/wilc_netdev.h b/drivers/net/wireless/mchp/wilc_netdev.h
new file mode 100644 (file)
index 0000000..d882ea3
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_NETDEV_H
+#define WILC_NETDEV_H
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_wlan_if.h"
+
+#define IP_STATE_OBTAINING             1
+#define IP_STATE_OBTAINED              2
+#define IP_STATE_GO_ASSIGNING          3
+#define IP_STATE_DEFAULT               4
+
+extern int wait_for_recovery;
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+void handle_pwrsave_for_IP(struct wilc_vif *vif, uint8_t state);
+void store_power_save_current_state(struct wilc_vif *vif, bool val);
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void clear_during_ip(struct timer_list *t);
+#else
+void clear_during_ip(unsigned long arg);
+#endif
+#endif //DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+struct net_device *wilc_get_if_netdev(struct wilc *wilc, uint8_t ifc);
+struct host_if_drv *get_drv_hndl_by_ifc(struct wilc *wilc, uint8_t ifc);
+
+#if KERNEL_VERSION(3, 14, 0) > LINUX_VERSION_CODE
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+       *(u32 *)dst = *(const u32 *)src;
+       *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
+#else
+       u16 *a = (u16 *)dst;
+       const u16 *b = (const u16 *)src;
+
+       a[0] = b[0];
+       a[1] = b[1];
+       a[2] = b[2];
+#endif
+}
+
+static inline bool ether_addr_equal_unaligned(const u8 *addr1, const u8 *addr2)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+       return ether_addr_equal(addr1, addr2);
+#else
+       return memcmp(addr1, addr2, ETH_ALEN) == 0;
+#endif
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) */
+
+int wilc_bt_power_up(struct wilc *wilc, int source);
+int wilc_bt_power_down(struct wilc *wilc, int source);
+void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size);
+
+#endif /* WILC_NETDEV_H */
diff --git a/drivers/net/wireless/mchp/wilc_sdio.c b/drivers/net/wireless/mchp/wilc_sdio.c
new file mode 100644 (file)
index 0000000..a3fc4fb
--- /dev/null
@@ -0,0 +1,1158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_wlan.h"
+
+enum sdio_host_lock {
+       WILC_SDIO_HOST_NO_TAKEN = 0,
+       WILC_SDIO_HOST_IRQ_TAKEN = 1,
+       WILC_SDIO_HOST_DIS_TAKEN = 2,
+};
+
+static enum sdio_host_lock     sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN;
+static wait_queue_head_t sdio_intr_waitqueue;
+
+#define SDIO_MODALIAS "wilc_sdio"
+
+#define SDIO_VENDOR_ID_WILC 0x0296
+#define SDIO_DEVICE_ID_WILC 0x5347
+
+static const struct sdio_device_id wilc_sdio_ids[] = {
+       { SDIO_DEVICE(SDIO_VENDOR_ID_WILC, SDIO_DEVICE_ID_WILC) },
+       { },
+};
+
+#define WILC_SDIO_BLOCK_SIZE 512
+
+struct wilc_sdio {
+       bool irq_gpio;
+       u32 block_size;
+       int nint;
+       bool is_init;
+};
+
+struct sdio_cmd52 {
+       u32 read_write:         1;
+       u32 function:           3;
+       u32 raw:                1;
+       u32 address:            17;
+       u32 data:               8;
+};
+
+struct sdio_cmd53 {
+       u32 read_write:         1;
+       u32 function:           3;
+       u32 block_mode:         1;
+       u32 increment:          1;
+       u32 address:            17;
+       u32 count:              9;
+       u8 *buffer;
+       u32 block_size;
+};
+
+static const struct wilc_hif_func wilc_hif_sdio;
+
+static void wilc_sdio_interrupt(struct sdio_func *func)
+{
+       if (sdio_intr_lock == WILC_SDIO_HOST_DIS_TAKEN)
+               return;
+       sdio_intr_lock = WILC_SDIO_HOST_IRQ_TAKEN;
+       sdio_release_host(func);
+       wilc_handle_isr(sdio_get_drvdata(func));
+       sdio_claim_host(func);
+       sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN;
+       wake_up_interruptible(&sdio_intr_waitqueue);
+}
+
+static int wilc_sdio_cmd52(struct wilc *wilc, struct sdio_cmd52 *cmd)
+{
+       struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
+       int ret;
+       u8 data;
+
+       sdio_claim_host(func);
+
+       func->num = cmd->function;
+       if (cmd->read_write) {  /* write */
+               if (cmd->raw) {
+                       sdio_writeb(func, cmd->data, cmd->address, &ret);
+                       data = sdio_readb(func, cmd->address, &ret);
+                       cmd->data = data;
+               } else {
+                       sdio_writeb(func, cmd->data, cmd->address, &ret);
+               }
+       } else {        /* read */
+               data = sdio_readb(func, cmd->address, &ret);
+               cmd->data = data;
+       }
+
+       sdio_release_host(func);
+
+       if (ret)
+               dev_err(&func->dev, "%s..failed, err(%d)\n", __func__, ret);
+       return ret;
+}
+
+static int wilc_sdio_cmd53(struct wilc *wilc, struct sdio_cmd53 *cmd)
+{
+       struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
+       int size, ret;
+
+       sdio_claim_host(func);
+
+       func->num = cmd->function;
+       func->cur_blksize = cmd->block_size;
+       if (cmd->block_mode)
+               size = cmd->count * cmd->block_size;
+       else
+               size = cmd->count;
+
+       if (cmd->read_write) {  /* write */
+               ret = sdio_memcpy_toio(func, cmd->address,
+                                      (void *)cmd->buffer, size);
+       } else {        /* read */
+               ret = sdio_memcpy_fromio(func, (void *)cmd->buffer,
+                                        cmd->address,  size);
+       }
+
+       sdio_release_host(func);
+
+       if (ret)
+               dev_err(&func->dev, "%s..failed, err(%d)\n", __func__,  ret);
+
+       return ret;
+}
+
+static int wilc_sdio_probe(struct sdio_func *func,
+                           const struct sdio_device_id *id)
+{
+       struct wilc *wilc;
+       int ret, io_type;
+       static bool init_power;
+       struct wilc_sdio *sdio_priv;
+
+       sdio_priv = kzalloc(sizeof(*sdio_priv), GFP_KERNEL);
+       if (!sdio_priv)
+               return -ENOMEM;
+
+       if (IS_ENABLED(CONFIG_WILC_HW_OOB_INTR))
+               io_type = WILC_HIF_SDIO_GPIO_IRQ;
+       else
+               io_type = WILC_HIF_SDIO;
+       dev_dbg(&func->dev, "Initializing netdev\n");
+       ret = wilc_netdev_init(&wilc, &func->dev, io_type, &wilc_hif_sdio);
+       if (ret) {
+               dev_err(&func->dev, "Couldn't initialize netdev\n");
+               kfree(sdio_priv);
+               return ret;
+       }
+       sdio_set_drvdata(func, wilc);
+       wilc->bus_data = sdio_priv;
+       wilc->dev = &func->dev;
+       wilc->dt_dev = &func->card->dev;
+
+       if (!init_power) {
+               wilc_wlan_power_on_sequence(wilc);
+               init_power = 1;
+       }
+
+       wilc_bt_init(wilc);
+
+       dev_info(&func->dev, "Driver Initializing success\n");
+       return 0;
+}
+
+static void wilc_sdio_remove(struct sdio_func *func)
+{
+       struct wilc *wilc = sdio_get_drvdata(func);
+
+       wilc_netdev_cleanup(wilc);
+       wilc_bt_deinit();
+}
+
+static int wilc_sdio_reset(struct wilc *wilc)
+{
+       struct sdio_cmd52 cmd;
+       int ret;
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+
+       dev_info(&func->dev, "De Init SDIO\n");
+
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 0;
+       cmd.address = 0x6;
+       cmd.data = 0x8;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret)
+               dev_err(&func->dev, "Fail cmd 52, reset cmd\n");
+       return ret;
+}
+
+static bool wilc_sdio_is_init(struct wilc *wilc)
+{
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+
+       return sdio_priv->is_init;
+}
+
+static int wilc_sdio_suspend(struct device *dev)
+{
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       struct wilc *wilc = sdio_get_drvdata(func);
+       int ret;
+
+       dev_info(&func->dev, "sdio suspend\n");
+       mutex_lock(&wilc->hif_cs);
+
+       chip_wakeup(wilc, 0);
+
+       if (mutex_is_locked(&wilc->hif_cs))
+               mutex_unlock(&wilc->hif_cs);
+
+       host_sleep_notify(wilc, 0);
+       chip_allow_sleep(wilc, 0);
+
+       mutex_lock(&wilc->hif_cs);
+
+       ret = wilc_sdio_reset(wilc);
+
+       return 0;
+}
+
+static int wilc_sdio_enable_interrupt(struct wilc *dev)
+{
+       struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
+       int ret = 0;
+
+       sdio_intr_lock  = WILC_SDIO_HOST_NO_TAKEN;
+
+       sdio_claim_host(func);
+       ret = sdio_claim_irq(func, wilc_sdio_interrupt);
+       sdio_release_host(func);
+
+       if (ret < 0) {
+               dev_err(&func->dev, "can't claim sdio_irq, err(%d)\n", ret);
+               ret = -EIO;
+       }
+       return ret;
+}
+
+static void wilc_sdio_disable_interrupt(struct wilc *dev)
+{
+       struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
+       int ret;
+
+       dev_info(&func->dev, "%s\n", __func__);
+
+       if (sdio_intr_lock  == WILC_SDIO_HOST_IRQ_TAKEN)
+               wait_event_interruptible(sdio_intr_waitqueue,
+                                  sdio_intr_lock == WILC_SDIO_HOST_NO_TAKEN);
+       sdio_intr_lock  = WILC_SDIO_HOST_DIS_TAKEN;
+
+       sdio_claim_host(func);
+       ret = sdio_release_irq(func);
+       if (ret < 0)
+               dev_err(&func->dev, "can't release sdio_irq, err(%d)\n", ret);
+       sdio_release_host(func);
+       sdio_intr_lock  = WILC_SDIO_HOST_NO_TAKEN;
+}
+
+/********************************************
+ *
+ *      Function 0
+ *
+ ********************************************/
+
+static int wilc_sdio_set_func0_csa_address(struct wilc *wilc, u32 adr)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct sdio_cmd52 cmd;
+       int ret;
+
+       /**
+        *      Review: BIG ENDIAN
+        **/
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 0;
+       cmd.address = 0x10c;
+       cmd.data = (u8)adr;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x10c data...\n");
+               goto fail;
+       }
+
+       cmd.address = 0x10d;
+       cmd.data = (u8)(adr >> 8);
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x10d data...\n");
+               goto fail;
+       }
+
+       cmd.address = 0x10e;
+       cmd.data = (u8)(adr >> 16);
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x10e data...\n");
+               goto fail;
+       }
+
+       return 1;
+fail:
+       return 0;
+}
+
+static int wilc_sdio_set_func0_block_size(struct wilc *wilc, u32 block_size)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct sdio_cmd52 cmd;
+       int ret;
+
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 0;
+       cmd.address = 0x10;
+       cmd.data = (u8)block_size;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x10 data...\n");
+               goto fail;
+       }
+
+       cmd.address = 0x11;
+       cmd.data = (u8)(block_size >> 8);
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x11 data...\n");
+               goto fail;
+       }
+
+       return 1;
+fail:
+       return 0;
+}
+
+/********************************************
+ *
+ *      Function 1
+ *
+ ********************************************/
+
+static int wilc_sdio_set_func1_block_size(struct wilc *wilc, u32 block_size)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct sdio_cmd52 cmd;
+       int ret;
+
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 0;
+       cmd.address = 0x110;
+       cmd.data = (u8)block_size;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x110 data...\n");
+               goto fail;
+       }
+       cmd.address = 0x111;
+       cmd.data = (u8)(block_size >> 8);
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Failed cmd52, set 0x111 data...\n");
+               goto fail;
+       }
+
+       return 1;
+fail:
+       return 0;
+}
+
+/********************************************
+ *
+ *      Sdio interfaces
+ *
+ ********************************************/
+static int wilc_sdio_write_reg(struct wilc *wilc, u32 addr, u32 data)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       int ret;
+
+       cpu_to_le32s(&data);
+
+       if (addr >= 0xf0 && addr <= 0xff) {
+               struct sdio_cmd52 cmd;
+
+               cmd.read_write = 1;
+               cmd.function = 0;
+               cmd.raw = 0;
+               cmd.address = addr;
+               cmd.data = data;
+               ret = wilc_sdio_cmd52(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd 52, write reg %08x ...\n", addr);
+                       goto fail;
+               }
+       } else {
+               struct sdio_cmd53 cmd;
+
+               /**
+                *      set the AHB address
+                **/
+               if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+                       goto fail;
+
+               cmd.read_write = 1;
+               cmd.function = 0;
+               cmd.address = 0x10f;
+               cmd.block_mode = 0;
+               cmd.increment = 1;
+               cmd.count = 4;
+               cmd.buffer = (u8 *)&data;
+               cmd.block_size = sdio_priv->block_size;
+               ret = wilc_sdio_cmd53(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd53, write reg (%08x)...\n", addr);
+                       goto fail;
+               }
+       }
+
+       return 1;
+
+fail:
+
+       return 0;
+}
+
+static int wilc_sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       u32 block_size = sdio_priv->block_size;
+       struct sdio_cmd53 cmd;
+       int nblk, nleft, ret;
+
+       cmd.read_write = 1;
+       if (addr > 0) {
+               /**
+                *      has to be word aligned...
+                **/
+               if (size & 0x3) {
+                       size += 4;
+                       size &= ~0x3;
+               }
+
+               /**
+                *      func 0 access
+                **/
+               cmd.function = 0;
+               cmd.address = 0x10f;
+       } else {
+               /**
+                *      has to be word aligned...
+                **/
+               if (size & 0x3) {
+                       size += 4;
+                       size &= ~0x3;
+               }
+
+               /**
+                *      func 1 access
+                **/
+               cmd.function = 1;
+               cmd.address = 0;
+       }
+
+       nblk = size / block_size;
+       nleft = size % block_size;
+
+       if (nblk > 0) {
+               cmd.block_mode = 1;
+               cmd.increment = 1;
+               cmd.count = nblk;
+               cmd.buffer = buf;
+               cmd.block_size = block_size;
+               if (addr > 0) {
+                       if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+                               goto fail;
+               }
+               ret = wilc_sdio_cmd53(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd53 [%x], block send...\n", addr);
+                       goto fail;
+               }
+               if (addr > 0)
+                       addr += nblk * block_size;
+               buf += nblk * block_size;
+       }
+
+       if (nleft > 0) {
+               cmd.block_mode = 0;
+               cmd.increment = 1;
+               cmd.count = nleft;
+               cmd.buffer = buf;
+
+               cmd.block_size = block_size;
+
+               if (addr > 0) {
+                       if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+                               goto fail;
+               }
+               ret = wilc_sdio_cmd53(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd53 [%x], bytes send...\n", addr);
+                       goto fail;
+               }
+       }
+
+       return 1;
+
+fail:
+
+       return 0;
+}
+
+static int wilc_sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       int ret;
+
+       if (addr >= 0xf0 && addr <= 0xff) {
+               struct sdio_cmd52 cmd;
+
+               cmd.read_write = 0;
+               cmd.function = 0;
+               cmd.raw = 0;
+               cmd.address = addr;
+               ret = wilc_sdio_cmd52(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd 52, read reg (%08x) ...\n", addr);
+                       goto fail;
+               }
+               *data = cmd.data;
+       } else {
+               struct sdio_cmd53 cmd;
+
+               if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+                       goto fail;
+
+               cmd.read_write = 0;
+               cmd.function = 0;
+               cmd.address = 0x10f;
+               cmd.block_mode = 0;
+               cmd.increment = 1;
+               cmd.count = 4;
+               cmd.buffer = (u8 *)data;
+
+               cmd.block_size = sdio_priv->block_size;
+               ret = wilc_sdio_cmd53(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd53, read reg (%08x)...\n", addr);
+                       goto fail;
+               }
+       }
+
+       le32_to_cpus(data);
+
+       return 1;
+
+fail:
+
+       return 0;
+}
+
+static int wilc_sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       u32 block_size = sdio_priv->block_size;
+       struct sdio_cmd53 cmd;
+       int nblk, nleft, ret;
+
+       cmd.read_write = 0;
+       if (addr > 0) {
+               /**
+                *      has to be word aligned...
+                **/
+               if (size & 0x3) {
+                       size += 4;
+                       size &= ~0x3;
+               }
+
+               /**
+                *      func 0 access
+                **/
+               cmd.function = 0;
+               cmd.address = 0x10f;
+       } else {
+               /**
+                *      has to be word aligned...
+                **/
+               if (size & 0x3) {
+                       size += 4;
+                       size &= ~0x3;
+               }
+
+               /**
+                *      func 1 access
+                **/
+               cmd.function = 1;
+               cmd.address = 0;
+       }
+
+       nblk = size / block_size;
+       nleft = size % block_size;
+
+       if (nblk > 0) {
+               cmd.block_mode = 1;
+               cmd.increment = 1;
+               cmd.count = nblk;
+               cmd.buffer = buf;
+               cmd.block_size = block_size;
+               if (addr > 0) {
+                       if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+                               goto fail;
+               }
+               ret = wilc_sdio_cmd53(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd53 [%x], block read...\n", addr);
+                       goto fail;
+               }
+               if (addr > 0)
+                       addr += nblk * block_size;
+               buf += nblk * block_size;
+       }       /* if (nblk > 0) */
+
+       if (nleft > 0) {
+               cmd.block_mode = 0;
+               cmd.increment = 1;
+               cmd.count = nleft;
+               cmd.buffer = buf;
+
+               cmd.block_size = block_size;
+
+               if (addr > 0) {
+                       if (!wilc_sdio_set_func0_csa_address(wilc, addr))
+                               goto fail;
+               }
+               ret = wilc_sdio_cmd53(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Failed cmd53 [%x], bytes read...\n", addr);
+                       goto fail;
+               }
+       }
+
+       return 1;
+
+fail:
+
+       return 0;
+}
+
+/********************************************
+ *
+ *      Bus interfaces
+ *
+ ********************************************/
+
+static int wilc_sdio_deinit(struct wilc *wilc)
+{
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+
+       sdio_priv->is_init = false;
+
+       return 1;
+}
+
+static int wilc_sdio_init(struct wilc *wilc, bool resume)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       struct sdio_cmd52 cmd;
+       int loop, ret;
+       u32 chipid;
+
+       dev_info(&func->dev, "SDIO speed: %d\n",
+               func->card->host->ios.clock);
+
+       /* Patch for sdio interrupt latency issue */
+       pm_runtime_get_sync(mmc_dev(func->card->host));
+
+       init_waitqueue_head(&sdio_intr_waitqueue);
+       sdio_priv->irq_gpio = (wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ);
+
+       /**
+        *      function 0 csa enable
+        **/
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 1;
+       cmd.address = 0x100;
+       cmd.data = 0x80;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Fail cmd 52, enable csa...\n");
+               goto fail;
+       }
+
+       /**
+        *      function 0 block size
+        **/
+       if (!wilc_sdio_set_func0_block_size(wilc, WILC_SDIO_BLOCK_SIZE)) {
+               dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n");
+               goto fail;
+       }
+       sdio_priv->block_size = WILC_SDIO_BLOCK_SIZE;
+
+       /**
+        *      enable func1 IO
+        **/
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 1;
+       cmd.address = 0x2;
+       cmd.data = 0x2;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev,
+                       "Fail cmd 52, set IOE register...\n");
+               goto fail;
+       }
+
+       /**
+        *      make sure func 1 is up
+        **/
+       cmd.read_write = 0;
+       cmd.function = 0;
+       cmd.raw = 0;
+       cmd.address = 0x3;
+       loop = 3;
+       do {
+               cmd.data = 0;
+               ret = wilc_sdio_cmd52(wilc, &cmd);
+               if (ret) {
+                       dev_err(&func->dev,
+                               "Fail cmd 52, get IOR register...\n");
+                       goto fail;
+               }
+               if (cmd.data == 0x2)
+                       break;
+       } while (loop--);
+
+       if (loop <= 0) {
+               dev_err(&func->dev, "Fail func 1 is not ready...\n");
+               goto fail;
+       }
+
+       /**
+        *      func 1 is ready, set func 1 block size
+        **/
+       if (!wilc_sdio_set_func1_block_size(wilc, WILC_SDIO_BLOCK_SIZE)) {
+               dev_err(&func->dev, "Fail set func 1 block size...\n");
+               goto fail;
+       }
+
+       /**
+        *      func 1 interrupt enable
+        **/
+       cmd.read_write = 1;
+       cmd.function = 0;
+       cmd.raw = 1;
+       cmd.address = 0x4;
+       cmd.data = 0x3;
+       ret = wilc_sdio_cmd52(wilc, &cmd);
+       if (ret) {
+               dev_err(&func->dev, "Fail cmd 52, set IEN register...\n");
+               goto fail;
+       }
+
+       /**
+        *      make sure can read back chip id correctly
+        **/
+       if (!resume) {
+               chipid = wilc_get_chipid(wilc, true);
+               if (is_wilc3000(chipid)) {
+                       wilc->chip = WILC_3000;
+               } else if (is_wilc1000(chipid)) {
+                       wilc->chip = WILC_1000;
+               } else {
+                       dev_err(&func->dev, "Unsupported chipid: %x\n", chipid);
+                       goto fail;
+               }
+               dev_info(&func->dev, "chipid %08x\n", chipid);
+       }
+
+       sdio_priv->is_init = true;
+
+       return 1;
+
+fail:
+
+       return 0;
+}
+
+static int wilc_sdio_read_size(struct wilc *wilc, u32 *size)
+{
+       u32 tmp;
+       struct sdio_cmd52 cmd;
+
+       /**
+        *      Read DMA count in words
+        **/
+       cmd.read_write = 0;
+       cmd.function = 0;
+       cmd.raw = 0;
+       cmd.address = 0xf2;
+       cmd.data = 0;
+       wilc_sdio_cmd52(wilc, &cmd);
+       tmp = cmd.data;
+
+       cmd.address = 0xf3;
+       cmd.data = 0;
+       wilc_sdio_cmd52(wilc, &cmd);
+       tmp |= (cmd.data << 8);
+
+       *size = tmp;
+       return 1;
+}
+
+static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       u32 tmp;
+       struct sdio_cmd52 cmd;
+       u32 irq_flags;
+       int i;
+
+       if (sdio_priv->irq_gpio) {
+               wilc_sdio_read_size(wilc, &tmp);
+
+               cmd.read_write = 0;
+               cmd.function = 1;
+               cmd.raw = 0;
+               cmd.data = 0;
+               if (wilc->chip == WILC_1000) {
+                       cmd.address = 0xf7;
+                       wilc_sdio_cmd52(wilc, &cmd);
+                       irq_flags = cmd.data & 0x1f;
+               } else {
+                       cmd.address = 0xfe;
+                       wilc_sdio_cmd52(wilc, &cmd);
+                       irq_flags = cmd.data & 0x0f;
+               }
+               tmp |= ((irq_flags >> 0) << IRG_FLAGS_OFFSET);
+
+               *int_status = tmp;
+       } else {
+               wilc_sdio_read_size(wilc, &tmp);
+               cmd.read_write = 0;
+               cmd.function = 1;
+               cmd.address = 0x04;
+               cmd.data = 0;
+               wilc_sdio_cmd52(wilc, &cmd);
+
+               if (cmd.data & BIT(0))
+                       tmp |= INT_0;
+               if (cmd.data & BIT(2))
+                       tmp |= INT_1;
+               if (cmd.data & BIT(3))
+                       tmp |= INT_2;
+               if (cmd.data & BIT(4))
+                       tmp |= INT_3;
+               if (cmd.data & BIT(5))
+                       tmp |= INT_4;
+
+               for (i = sdio_priv->nint; i < MAX_NUM_INT; i++) {
+                       if ((tmp >> (IRG_FLAGS_OFFSET + i)) & 0x1) {
+                               dev_err(&func->dev,
+                                       "Unexpected interrupt (1) : tmp=%x, data=%x\n",
+                                       tmp, cmd.data);
+                               break;
+                       }
+               }
+
+               *int_status = tmp;
+
+       }
+
+       return 1;
+}
+
+static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       int ret;
+       u32 reg = 0;
+
+       if (wilc->chip == WILC_1000) {
+               if (sdio_priv->irq_gpio)
+                       reg = val & (BIT(MAX_NUM_INT) - 1);
+
+               /* select VMM table 0 */
+               if (val & SEL_VMM_TBL0)
+                       reg |= BIT(5);
+               /* select VMM table 1 */
+               if (val & SEL_VMM_TBL1)
+                       reg |= BIT(6);
+               /* enable VMM */
+               if (val & EN_VMM)
+                       reg |= BIT(7);
+               if (reg) {
+                       struct sdio_cmd52 cmd;
+
+                       cmd.read_write = 1;
+                       cmd.function = 0;
+                       cmd.raw = 0;
+                       cmd.address = 0xf8;
+                       cmd.data = reg;
+
+                       ret = wilc_sdio_cmd52(wilc, &cmd);
+                       if (ret) {
+                               dev_err(&func->dev,
+                                       "Failed cmd52, set 0xf8 data (%d) ...\n",
+                                       __LINE__);
+                               goto fail;
+                       }
+               }
+       } else {
+               if (sdio_priv->irq_gpio) {
+                       reg = val & (BIT(MAX_NUM_INT) - 1);
+                       if (reg) {
+                               struct sdio_cmd52 cmd;
+
+                               cmd.read_write = 1;
+                               cmd.function = 0;
+                               cmd.raw = 0;
+                               cmd.address = 0xfe;
+                               cmd.data = reg;
+
+                               ret = wilc_sdio_cmd52(wilc, &cmd);
+                               if (ret) {
+                                       dev_err(&func->dev,
+                                               "Failed cmd52, set 0xf8 data (%d) ...\n",
+                                               __LINE__);
+                                       goto fail;
+                               }
+                       }
+               }
+               /* select VMM table 0 */
+               if (val & SEL_VMM_TBL0)
+                       reg |= BIT(0);
+               /* select VMM table 1 */
+               if (val & SEL_VMM_TBL1)
+                       reg |= BIT(1);
+               /* enable VMM */
+               if (val & EN_VMM)
+                       reg |= BIT(2);
+
+               if (reg) {
+                       struct sdio_cmd52 cmd;
+
+                       cmd.read_write = 1;
+                       cmd.function = 0;
+                       cmd.raw = 0;
+                       cmd.address = 0xf1;
+                       cmd.data = reg;
+
+                       ret = wilc_sdio_cmd52(wilc, &cmd);
+                       if (ret) {
+                               dev_err(&func->dev,
+                                       "Failed cmd52, set 0xf6 data (%d) ...\n",
+                                       __LINE__);
+                               goto fail;
+                       }
+               }
+       }
+
+       return 1;
+fail:
+       return 0;
+}
+
+static int wilc_sdio_sync_ext(struct wilc *wilc, int nint)
+{
+       struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+       struct wilc_sdio *sdio_priv = wilc->bus_data;
+       u32 reg;
+       int ret, i;
+
+       if (nint > MAX_NUM_INT) {
+               dev_err(&func->dev, "Too many interrupts %d\n", nint);
+               return 0;
+       }
+
+       sdio_priv->nint = nint;
+
+/* WILC3000 only. Was removed in WILC1000 on revision 6200.
+ * Might be related to suspend/resume
+ */
+       if (wilc->chip == WILC_3000) {
+               /**
+                *      Disable power sequencer
+                **/
+               if (!wilc_sdio_read_reg(wilc, WILC_MISC, &reg)) {
+                       dev_err(&func->dev, "Failed read misc reg\n");
+                       return 0;
+               }
+               reg &= ~BIT(8);
+               if (!wilc_sdio_write_reg(wilc, WILC_MISC, reg)) {
+                       dev_err(&func->dev, "Failed write misc reg\n");
+                       return 0;
+               }
+       }
+
+       if (sdio_priv->irq_gpio) {
+               /**
+                *      interrupt pin mux select
+                **/
+               ret = wilc_sdio_read_reg(wilc, WILC_PIN_MUX_0, &reg);
+               if (!ret) {
+                       dev_err(&func->dev, "Failed read reg (%08x)...\n",
+                               WILC_PIN_MUX_0);
+                       return 0;
+               }
+               reg |= BIT(8);
+               ret = wilc_sdio_write_reg(wilc, WILC_PIN_MUX_0, reg);
+               if (!ret) {
+                       dev_err(&func->dev, "Failed write reg (%08x)...\n",
+                               WILC_PIN_MUX_0);
+                       return 0;
+               }
+
+               /**
+                *      interrupt enable
+                **/
+               ret = wilc_sdio_read_reg(wilc, WILC_INTR_ENABLE, &reg);
+               if (!ret) {
+                       dev_err(&func->dev, "Failed read reg (%08x)...\n",
+                               WILC_INTR_ENABLE);
+                       return 0;
+               }
+
+               for (i = 0; (i < 5) && (nint > 0); i++, nint--)
+                       reg |= BIT((27 + i));
+               ret = wilc_sdio_write_reg(wilc, WILC_INTR_ENABLE, reg);
+               if (!ret) {
+                       dev_err(&func->dev, "Failed write reg (%08x)...\n",
+                               WILC_INTR_ENABLE);
+                       return 0;
+               }
+               if (nint) {
+                       ret = wilc_sdio_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
+                       if (!ret) {
+                               dev_err(&func->dev,
+                                       "Failed read reg (%08x)...\n",
+                                       WILC_INTR2_ENABLE);
+                               return 0;
+                       }
+
+                       for (i = 0; (i < 3) && (nint > 0); i++, nint--)
+                               reg |= BIT(i);
+
+                       ret = wilc_sdio_write_reg(wilc, WILC_INTR2_ENABLE, reg);
+                       if (!ret) {
+                               dev_err(&func->dev,
+                                       "Failed write reg (%08x)...\n",
+                                       WILC_INTR2_ENABLE);
+                               return 0;
+                       }
+               }
+       }
+       return 1;
+}
+
+/* Global sdio HIF function table */
+static const struct wilc_hif_func wilc_hif_sdio = {
+       .hif_init = wilc_sdio_init,
+       .hif_deinit = wilc_sdio_deinit,
+       .hif_read_reg = wilc_sdio_read_reg,
+       .hif_write_reg = wilc_sdio_write_reg,
+       .hif_block_rx = wilc_sdio_read,
+       .hif_block_tx = wilc_sdio_write,
+       .hif_read_int = wilc_sdio_read_int,
+       .hif_clear_int_ext = wilc_sdio_clear_int_ext,
+       .hif_read_size = wilc_sdio_read_size,
+       .hif_block_tx_ext = wilc_sdio_write,
+       .hif_block_rx_ext = wilc_sdio_read,
+       .hif_sync_ext = wilc_sdio_sync_ext,
+       .enable_interrupt = wilc_sdio_enable_interrupt,
+       .disable_interrupt = wilc_sdio_disable_interrupt,
+       .hif_reset = wilc_sdio_reset,
+       .hif_is_init = wilc_sdio_is_init,
+};
+
+static int wilc_sdio_resume(struct device *dev)
+{
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       struct wilc *wilc = sdio_get_drvdata(func);
+
+       dev_info(&func->dev, "sdio resume\n");
+       chip_wakeup(wilc, 0);
+       wilc_sdio_init(wilc, true);
+
+       if (mutex_is_locked(&wilc->hif_cs))
+               mutex_unlock(&wilc->hif_cs);
+
+       host_wakeup_notify(wilc, 0);
+
+       mutex_lock(&wilc->hif_cs);
+
+       chip_allow_sleep(wilc, 0);
+
+       if (mutex_is_locked(&wilc->hif_cs))
+               mutex_unlock(&wilc->hif_cs);
+
+       return 0;
+}
+
+static const struct of_device_id wilc_of_match[] = {
+       { .compatible = "microchip,wilc1000", },
+       { .compatible = "microchip,wilc3000", },
+       { /* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, wilc_of_match);
+
+static const struct dev_pm_ops wilc_sdio_pm_ops = {
+       .suspend = wilc_sdio_suspend,
+       .resume = wilc_sdio_resume,
+};
+
+static struct sdio_driver wilc_sdio_driver = {
+       .name           = SDIO_MODALIAS,
+       .id_table       = wilc_sdio_ids,
+       .probe          = wilc_sdio_probe,
+       .remove         = wilc_sdio_remove,
+       .drv = {
+               .pm = &wilc_sdio_pm_ops,
+               .of_match_table = wilc_of_match,
+       }
+};
+module_driver(wilc_sdio_driver,
+             sdio_register_driver,
+             sdio_unregister_driver);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/wireless/mchp/wilc_spi.c b/drivers/net/wireless/mchp/wilc_spi.c
new file mode 100644 (file)
index 0000000..c20fbf4
--- /dev/null
@@ -0,0 +1,1231 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include "wilc_wfi_netdevice.h"
+
+struct wilc_spi {
+       int crc_off;
+       int nint;
+       bool is_init;
+};
+
+static const struct wilc_hif_func wilc_hif_spi;
+
+static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen);
+static int wilc_spi_reset(struct wilc *wilc);
+
+/********************************************
+ *
+ *      Crc7
+ *
+ ********************************************/
+
+static const u8 crc7_syndrome_table[256] = {
+       0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
+       0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
+       0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
+       0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
+       0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
+       0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
+       0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
+       0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
+       0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
+       0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
+       0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
+       0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
+       0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
+       0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
+       0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
+       0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
+       0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
+       0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
+       0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
+       0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
+       0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
+       0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
+       0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
+       0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
+       0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
+       0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
+       0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
+       0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
+       0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
+       0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
+       0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
+       0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
+};
+
+static u8 crc7_byte(u8 crc, u8 data)
+{
+       return crc7_syndrome_table[(crc << 1) ^ data];
+}
+
+static u8 crc7(u8 crc, const u8 *buffer, u32 len)
+{
+       while (len--)
+               crc = crc7_byte(crc, *buffer++);
+       return crc;
+}
+
+/********************************************
+ *
+ *      Spi protocol Function
+ *
+ ********************************************/
+
+#define CMD_DMA_WRITE                          0xc1
+#define CMD_DMA_READ                           0xc2
+#define CMD_INTERNAL_WRITE                     0xc3
+#define CMD_INTERNAL_READ                      0xc4
+#define CMD_TERMINATE                          0xc5
+#define CMD_REPEAT                             0xc6
+#define CMD_DMA_EXT_WRITE                      0xc7
+#define CMD_DMA_EXT_READ                       0xc8
+#define CMD_SINGLE_WRITE                       0xc9
+#define CMD_SINGLE_READ                                0xca
+#define CMD_RESET                              0xcf
+
+#define N_OK                                   1
+#define N_FAIL                                 0
+#define N_RESET                                        -1
+#define N_RETRY                                        -2
+
+#define SPI_RESP_RETRY_COUNT                   (10)
+#define SPI_RETRY_COUNT                                (10)
+#define DATA_PKT_SZ_256                                256
+#define DATA_PKT_SZ_512                                512
+#define DATA_PKT_SZ_1K                         1024
+#define DATA_PKT_SZ_2K                         (2 * 1024)
+#define DATA_PKT_SZ_4K                         (4 * 1024)
+#define DATA_PKT_SZ_8K                         (8 * 1024)
+#define DATA_PKT_SZ                            DATA_PKT_SZ_8K
+
+#define USE_SPI_DMA                            0
+
+static int wilc_bus_probe(struct spi_device *spi)
+{
+       int ret;
+       static bool init_power;
+       struct wilc *wilc;
+       struct device *dev = &spi->dev;
+       struct wilc_spi *spi_priv;
+
+       dev_info(&spi->dev, "spiModalias: %s, spiMax-Speed: %d\n",
+                       spi->modalias, spi->max_speed_hz);
+
+       spi_priv = kzalloc(sizeof(*spi_priv), GFP_KERNEL);
+       if (!spi_priv)
+               return -ENOMEM;
+
+       ret = wilc_netdev_init(&wilc, dev, WILC_HIF_SPI, &wilc_hif_spi);
+       if (ret) {
+               kfree(spi_priv);
+               return ret;
+       }
+
+       spi_set_drvdata(spi, wilc);
+       wilc->dev = &spi->dev;
+       wilc->bus_data = spi_priv;
+       wilc->dt_dev = &spi->dev;
+
+
+       if (!init_power) {
+               wilc_wlan_power_on_sequence(wilc);
+               init_power = 1;
+       }
+
+       wilc_bt_init(wilc);
+
+       dev_info(dev, "WILC SPI probe success\n");
+       return 0;
+}
+
+static int wilc_bus_remove(struct spi_device *spi)
+{
+       struct wilc *wilc = spi_get_drvdata(spi);
+
+       wilc_netdev_cleanup(wilc);
+       wilc_bt_deinit();
+       return 0;
+}
+
+static int wilc_spi_suspend(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct wilc *wilc = spi_get_drvdata(spi);
+
+       dev_info(&spi->dev, "\n\n << SUSPEND >>\n\n");
+       mutex_lock(&wilc->hif_cs);
+       chip_wakeup(wilc, 0);
+
+       if (mutex_is_locked(&wilc->hif_cs))
+               mutex_unlock(&wilc->hif_cs);
+
+       /*notify the chip that host will sleep*/
+       host_sleep_notify(wilc, 0);
+       chip_allow_sleep(wilc, 0);
+       mutex_lock(&wilc->hif_cs);
+
+       return 0;
+}
+
+static int wilc_spi_resume(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct wilc *wilc = spi_get_drvdata(spi);
+
+       dev_info(&spi->dev, "\n\n  <<RESUME>>\n\n");
+
+       /*wake the chip to compelete the re-intialization*/
+       chip_wakeup(wilc, 0);
+
+       if (mutex_is_locked(&wilc->hif_cs))
+               mutex_unlock(&wilc->hif_cs);
+
+       host_wakeup_notify(wilc, 0);
+
+       mutex_lock(&wilc->hif_cs);
+
+       chip_allow_sleep(wilc, 0);
+
+       if (mutex_is_locked(&wilc->hif_cs))
+               mutex_unlock(&wilc->hif_cs);
+
+       return 0;
+}
+
+static const struct of_device_id wilc_of_match[] = {
+       { .compatible = "microchip,wilc1000", },
+       { .compatible = "microchip,wilc3000", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, wilc_of_match);
+static const struct dev_pm_ops wilc_spi_pm_ops = {
+       .suspend = wilc_spi_suspend,
+       .resume = wilc_spi_resume,
+};
+
+static struct spi_driver wilc_spi_driver = {
+       .driver = {
+               .name = MODALIAS,
+               .of_match_table = wilc_of_match,
+               .pm = &wilc_spi_pm_ops,
+       },
+       .probe =  wilc_bus_probe,
+       .remove = wilc_bus_remove,
+};
+module_spi_driver(wilc_spi_driver);
+MODULE_LICENSE("GPL");
+
+static int spi_data_rsp(struct wilc *wilc, u8 cmd)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       struct wilc_spi *spi_priv = wilc->bus_data;
+       u8 len;
+       u8 rsp[3];
+       int result = N_OK;
+
+       if (!spi_priv->crc_off)
+               len = 2;
+       else
+               len = 3;
+
+       if (wilc_spi_rx(wilc, &rsp[0], len)) {
+               dev_err(&spi->dev, "Failed bus error...\n");
+               result = N_FAIL;
+               goto fail;
+       }
+
+       if ((rsp[len-1] != 0) || (rsp[len-2] != 0xC3)) {
+               dev_err(&spi->dev, "Failed data response read, %x %x %x\n",
+                       rsp[0], rsp[1], rsp[2]);
+               result = N_FAIL;
+               goto fail;
+       }
+
+fail:
+       return result;
+}
+
+static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int ret;
+       struct spi_message msg;
+
+       if (len > 0 && b) {
+               struct spi_transfer tr = {
+                       .tx_buf = b,
+                       .len = len,
+                       .delay_usecs = 0,
+               };
+               char *r_buffer = kzalloc(len, GFP_KERNEL);
+
+               if (!r_buffer)
+                       return -ENOMEM;
+
+               tr.rx_buf = r_buffer;
+               dev_dbg(&spi->dev, "Request writing %d bytes\n", len);
+
+               memset(&msg, 0, sizeof(msg));
+               spi_message_init(&msg);
+               msg.spi = spi;
+               msg.is_dma_mapped = USE_SPI_DMA;
+               spi_message_add_tail(&tr, &msg);
+
+               ret = spi_sync(spi, &msg);
+               if (ret < 0)
+                       dev_err(&spi->dev, "SPI transaction failed\n");
+
+               kfree(r_buffer);
+       } else {
+               dev_err(&spi->dev,
+                       "can't write data with the following length: %d\n",
+                       len);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int ret;
+
+       if (rlen > 0) {
+               struct spi_message msg;
+               struct spi_transfer tr = {
+                       .rx_buf = rb,
+                       .len = rlen,
+                       .delay_usecs = 0,
+
+               };
+               char *t_buffer = kzalloc(rlen, GFP_KERNEL);
+
+               if (!t_buffer)
+                       return -ENOMEM;
+
+               tr.tx_buf = t_buffer;
+
+               memset(&msg, 0, sizeof(msg));
+               spi_message_init(&msg);
+               msg.spi = spi;
+               msg.is_dma_mapped = USE_SPI_DMA;
+               spi_message_add_tail(&tr, &msg);
+
+               ret = spi_sync(spi, &msg);
+               if (ret < 0)
+                       dev_err(&spi->dev, "SPI transaction failed\n");
+               kfree(t_buffer);
+       } else {
+               dev_err(&spi->dev,
+                       "can't read data with the following length: %u\n",
+                       rlen);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int ret;
+
+       if (rlen > 0) {
+               struct spi_message msg;
+               struct spi_transfer tr = {
+                       .rx_buf = rb,
+                       .tx_buf = wb,
+                       .len = rlen,
+                       .bits_per_word = 8,
+                       .delay_usecs = 0,
+
+               };
+
+               memset(&msg, 0, sizeof(msg));
+               spi_message_init(&msg);
+               msg.spi = spi;
+               msg.is_dma_mapped = USE_SPI_DMA;
+
+               spi_message_add_tail(&tr, &msg);
+               ret = spi_sync(spi, &msg);
+               if (ret < 0)
+                       dev_err(&spi->dev, "SPI transaction failed\n");
+       } else {
+               dev_err(&spi->dev,
+                       "can't read data with the following length: %u\n",
+                       rlen);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
+                           u8 clockless)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       struct wilc_spi *spi_priv = wilc->bus_data;
+       u8 wb[32], rb[32];
+       u8 wix, rix;
+       u32 len2;
+       u8 rsp;
+       int len = 0;
+       int result = N_OK;
+       int retry;
+       u8 crc[2];
+
+       wb[0] = cmd;
+       switch (cmd) {
+       case CMD_SINGLE_READ: /* single word (4 bytes) read */
+               wb[1] = (u8)(adr >> 16);
+               wb[2] = (u8)(adr >> 8);
+               wb[3] = (u8)adr;
+               len = 5;
+               break;
+
+       case CMD_INTERNAL_READ: /* internal register read */
+               wb[1] = (u8)(adr >> 8);
+               if (clockless == 1)
+                       wb[1] |= BIT(7);
+               wb[2] = (u8)adr;
+               wb[3] = 0x00;
+               len = 5;
+               break;
+
+       case CMD_TERMINATE:
+               wb[1] = 0x00;
+               wb[2] = 0x00;
+               wb[3] = 0x00;
+               len = 5;
+               break;
+
+       case CMD_REPEAT:
+               wb[1] = 0x00;
+               wb[2] = 0x00;
+               wb[3] = 0x00;
+               len = 5;
+               break;
+
+       case CMD_RESET:
+               wb[1] = 0xff;
+               wb[2] = 0xff;
+               wb[3] = 0xff;
+               len = 5;
+               break;
+
+       case CMD_DMA_WRITE: /* dma write */
+       case CMD_DMA_READ:  /* dma read */
+               wb[1] = (u8)(adr >> 16);
+               wb[2] = (u8)(adr >> 8);
+               wb[3] = (u8)adr;
+               wb[4] = (u8)(sz >> 8);
+               wb[5] = (u8)(sz);
+               len = 7;
+               break;
+
+       case CMD_DMA_EXT_WRITE: /* dma extended write */
+       case CMD_DMA_EXT_READ:  /* dma extended read */
+               wb[1] = (u8)(adr >> 16);
+               wb[2] = (u8)(adr >> 8);
+               wb[3] = (u8)adr;
+               wb[4] = (u8)(sz >> 16);
+               wb[5] = (u8)(sz >> 8);
+               wb[6] = (u8)(sz);
+               len = 8;
+               break;
+
+       case CMD_INTERNAL_WRITE: /* internal register write */
+               wb[1] = (u8)(adr >> 8);
+               if (clockless == 1)
+                       wb[1] |= BIT(7);
+               wb[2] = (u8)(adr);
+               wb[3] = b[3];
+               wb[4] = b[2];
+               wb[5] = b[1];
+               wb[6] = b[0];
+               len = 8;
+               break;
+
+       case CMD_SINGLE_WRITE: /* single word write */
+               wb[1] = (u8)(adr >> 16);
+               wb[2] = (u8)(adr >> 8);
+               wb[3] = (u8)(adr);
+               wb[4] = b[3];
+               wb[5] = b[2];
+               wb[6] = b[1];
+               wb[7] = b[0];
+               len = 9;
+               break;
+
+       default:
+               result = N_FAIL;
+               break;
+       }
+
+       if (result != N_OK)
+               return result;
+
+       if (!spi_priv->crc_off)
+               wb[len - 1] = (crc7(0x7f, (const u8 *)&wb[0], len - 1)) << 1;
+       else
+               len -= 1;
+
+#define NUM_SKIP_BYTES (1)
+#define NUM_RSP_BYTES (2)
+#define NUM_DATA_HDR_BYTES (1)
+#define NUM_DATA_BYTES (4)
+#define NUM_CRC_BYTES (2)
+#define NUM_DUMMY_BYTES (3)
+       if (cmd == CMD_RESET ||
+           cmd == CMD_TERMINATE ||
+           cmd == CMD_REPEAT) {
+               len2 = len + (NUM_SKIP_BYTES + NUM_RSP_BYTES + NUM_DUMMY_BYTES);
+       } else if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
+               int tmp = NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES
+                       + NUM_DUMMY_BYTES;
+               if (!spi_priv->crc_off)
+                       len2 = len + tmp + NUM_CRC_BYTES;
+               else
+                       len2 = len + tmp;
+       } else {
+               len2 = len + (NUM_RSP_BYTES + NUM_DUMMY_BYTES);
+       }
+#undef NUM_DUMMY_BYTES
+
+       if (len2 > ARRAY_SIZE(wb)) {
+               dev_err(&spi->dev, "spi buffer size too small (%d) (%zu)\n",
+                       len2, ARRAY_SIZE(wb));
+               return N_FAIL;
+       }
+       /* zero spi write buffers. */
+       for (wix = len; wix < len2; wix++)
+               wb[wix] = 0;
+       rix = len;
+
+       if (wilc_spi_tx_rx(wilc, wb, rb, len2)) {
+               dev_err(&spi->dev, "Failed cmd write, bus error...\n");
+               return N_FAIL;
+       }
+
+       /*
+        * Command/Control response
+        */
+       if (cmd == CMD_RESET || cmd == CMD_TERMINATE || cmd == CMD_REPEAT)
+               rix++; /* skip 1 byte */
+
+       rsp = rb[rix++];
+
+       if (rsp != cmd) {
+               dev_err(&spi->dev,
+                       "Failed cmd response, cmd (%02x), resp (%02x)\n",
+                       cmd, rsp);
+               return N_FAIL;
+       }
+
+       /*
+        * State response
+        */
+       rsp = rb[rix++];
+       if (rsp != 0x00) {
+               dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+                       rsp);
+               return N_FAIL;
+       }
+
+       if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ ||
+           cmd == CMD_DMA_READ || cmd == CMD_DMA_EXT_READ) {
+               /*
+                * Data Respnose header
+                */
+               retry = SPI_RESP_RETRY_COUNT;
+               do {
+                       /*
+                        * ensure there is room in buffer later
+                        * to read data and crc
+                        */
+                       if (rix < len2) {
+                               rsp = rb[rix++];
+                       } else {
+                               retry = 0;
+                               break;
+                       }
+                       if (((rsp >> 4) & 0xf) == 0xf)
+                               break;
+               } while (retry--);
+
+               if (retry <= 0) {
+                       dev_err(&spi->dev,
+                               "Error, data read response (%02x)\n", rsp);
+                       return N_RESET;
+               }
+       }
+
+       if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) {
+               /*
+                * Read bytes
+                */
+               if ((rix + 3) < len2) {
+                       b[0] = rb[rix++];
+                       b[1] = rb[rix++];
+                       b[2] = rb[rix++];
+                       b[3] = rb[rix++];
+               } else {
+                       dev_err(&spi->dev,
+                               "buffer overrun when reading data.\n");
+                       return N_FAIL;
+               }
+
+               if (!spi_priv->crc_off) {
+                       /*
+                        * Read Crc
+                        */
+                       if ((rix + 1) < len2) {
+                               crc[0] = rb[rix++];
+                               crc[1] = rb[rix++];
+                       } else {
+                               dev_err(&spi->dev,
+                                       "buffer overrun when reading crc.\n");
+                               return N_FAIL;
+                       }
+               }
+       } else if ((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) {
+               int ix;
+
+               /* some data may be read in response to dummy bytes. */
+               for (ix = 0; (rix < len2) && (ix < sz); )
+                       b[ix++] = rb[rix++];
+
+               sz -= ix;
+
+               if (sz > 0) {
+                       int nbytes;
+
+                       if (sz <= (DATA_PKT_SZ - ix))
+                               nbytes = sz;
+                       else
+                               nbytes = DATA_PKT_SZ - ix;
+
+                       /*
+                        * Read bytes
+                        */
+                       if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+                               dev_err(&spi->dev,
+                                       "Failed block read, bus err\n");
+                               return N_FAIL;
+                       }
+
+                       /*
+                        * Read Crc
+                        */
+                       if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) {
+                               dev_err(&spi->dev,
+                                       "Failed block crc read, bus err\n");
+                               return N_FAIL;
+                       }
+
+                       ix += nbytes;
+                       sz -= nbytes;
+               }
+
+               /*
+                * if any data in left unread,
+                * then read the rest using normal DMA code.
+                */
+               while (sz > 0) {
+                       int nbytes;
+
+                       if (sz <= DATA_PKT_SZ)
+                               nbytes = sz;
+                       else
+                               nbytes = DATA_PKT_SZ;
+
+                       /*
+                        * read data response only on the next DMA cycles not
+                        * the first DMA since data response header is already
+                        * handled above for the first DMA.
+                        */
+                       /*
+                        * Data Respnose header
+                        */
+                       retry = SPI_RESP_RETRY_COUNT;
+                       do {
+                               if (wilc_spi_rx(wilc, &rsp, 1)) {
+                                       dev_err(&spi->dev,
+                                               "Failed resp read, bus err\n");
+                                       result = N_FAIL;
+                                       break;
+                               }
+                               if (((rsp >> 4) & 0xf) == 0xf)
+                                       break;
+                       } while (retry--);
+
+                       if (result == N_FAIL)
+                               break;
+
+                       /*
+                        * Read bytes
+                        */
+                       if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+                               dev_err(&spi->dev,
+                                       "Failed block read, bus err\n");
+                               result = N_FAIL;
+                               break;
+                       }
+
+                       /*
+                        * Read Crc
+                        */
+                       if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) {
+                               dev_err(&spi->dev,
+                                       "Failed block crc read, bus err\n");
+                               result = N_FAIL;
+                               break;
+                       }
+
+                       ix += nbytes;
+                       sz -= nbytes;
+               }
+       }
+       return result;
+}
+
+static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       struct wilc_spi *spi_priv = wilc->bus_data;
+       int ix, nbytes;
+       int result = 1;
+       u8 cmd, order, crc[2] = {0};
+
+       /*
+        * Data
+        */
+       ix = 0;
+       do {
+               if (sz <= DATA_PKT_SZ) {
+                       nbytes = sz;
+                       order = 0x3;
+               } else {
+                       nbytes = DATA_PKT_SZ;
+                       if (ix == 0)
+                               order = 0x1;
+                       else
+                               order = 0x02;
+               }
+
+               /*
+                * Write command
+                */
+               cmd = 0xf0;
+               cmd |= order;
+
+               if (wilc_spi_tx(wilc, &cmd, 1)) {
+                       dev_err(&spi->dev,
+                               "Failed data block cmd write, bus error...\n");
+                       result = N_FAIL;
+                       break;
+               }
+
+               /*
+                * Write data
+                */
+               if (wilc_spi_tx(wilc, &b[ix], nbytes)) {
+                       dev_err(&spi->dev,
+                               "Failed data block write, bus error...\n");
+                       result = N_FAIL;
+                       break;
+               }
+
+               /*
+                * Write Crc
+                */
+               if (!spi_priv->crc_off) {
+                       if (wilc_spi_tx(wilc, crc, 2)) {
+                               dev_err(&spi->dev, "Failed data block crc write, bus error...\n");
+                               result = N_FAIL;
+                               break;
+                       }
+               }
+
+               /*
+                * No need to wait for response
+                */
+               ix += nbytes;
+               sz -= nbytes;
+       } while (sz);
+
+       return result;
+}
+
+/********************************************
+ *
+ *      Spi Internal Read/Write Function
+ *
+ ********************************************/
+
+static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int result;
+       u8 retry = SPI_RETRY_COUNT;
+
+retry:
+       cpu_to_le32s(&dat);
+       result = spi_cmd_complete(wilc, CMD_INTERNAL_WRITE, adr, (u8 *)&dat, 4,
+                                 0);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed internal write cmd...\n");
+               goto fail;
+       }
+
+fail:
+       if (result != N_OK) {
+               usleep_range(1000, 1100);
+               wilc_spi_reset(wilc);
+               dev_err(&spi->dev, "Reset and retry %d %x\n", retry, adr);
+               usleep_range(1000, 1100);
+               retry--;
+               if (retry)
+                       goto retry;
+       }
+       return result;
+}
+
+static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int result = N_OK;
+       u8 retry = SPI_RETRY_COUNT;
+
+retry:
+       result = spi_cmd_complete(wilc, CMD_INTERNAL_READ, adr, (u8 *)data, 4,
+                                 0);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed internal read cmd...\n");
+               goto fail;
+       }
+
+       le32_to_cpus(data);
+
+fail:
+       if (result != N_OK) {
+               usleep_range(1000, 1100);
+               wilc_spi_reset(wilc);
+               dev_err(&spi->dev, "Reset and retry %d %x\n", retry, adr);
+               usleep_range(1000, 1100);
+               retry--;
+               if (retry)
+                       goto retry;
+       }
+       return result;
+}
+
+/********************************************
+ *
+ *      Spi interfaces
+ *
+ ********************************************/
+
+static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       u8 retry = SPI_RETRY_COUNT;
+       int result = N_OK;
+       u8 cmd = CMD_SINGLE_WRITE;
+       u8 clockless = 0;
+
+       cpu_to_le32s(&data);
+_RETRY_:
+       if (addr <= 0x30) {
+               /* Clockless register */
+               cmd = CMD_INTERNAL_WRITE;
+               clockless = 1;
+       } else {
+               cmd = CMD_SINGLE_WRITE;
+               clockless = 0;
+       }
+
+       result = spi_cmd_complete(wilc, cmd, addr, (u8 *)&data, 4, clockless);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr);
+               goto fail;
+       }
+
+fail:
+       if (result != N_OK) {
+               usleep_range(1000, 1100);
+               wilc_spi_reset(wilc);
+               dev_err(&spi->dev,
+                       "Reset and retry %d %x %d\n", retry, addr, data);
+               usleep_range(1000, 1100);
+               retry--;
+               if (retry)
+                       goto _RETRY_;
+       }
+       return result;
+}
+
+static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int result;
+       u8 retry = SPI_RETRY_COUNT;
+
+       /*
+        * has to be greated than 4
+        */
+       if (size <= 4)
+               return 0;
+
+retry:
+       result = spi_cmd_complete(wilc, CMD_DMA_EXT_WRITE, addr, NULL, size, 0);
+       if (result != N_OK) {
+               dev_err(&spi->dev,
+                       "Failed cmd, write block (%08x)...\n", addr);
+               goto fail;
+       }
+
+       /*
+        * Data
+        */
+       result = spi_data_write(wilc, buf, size);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed block data write...\n");
+               goto fail;
+       }
+       /*
+        * Data RESP
+        */
+       result = spi_data_rsp(wilc, CMD_DMA_EXT_WRITE);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed block data write...\n");
+               goto fail;
+       }
+
+fail:
+       if (result != N_OK) {
+               usleep_range(1000, 1100);
+               wilc_spi_reset(wilc);
+               dev_err(&spi->dev,
+                       "Reset and retry %d %x %d\n", retry, addr, size);
+               usleep_range(1000, 1100);
+               retry--;
+               if (retry)
+                       goto retry;
+       }
+       return result;
+}
+
+static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       u8 retry = SPI_RETRY_COUNT;
+       int result = N_OK;
+       u8 cmd = CMD_SINGLE_READ;
+       u8 clockless = 0;
+
+retry:
+       if (addr <= 0x30) {
+               /* Clockless register */
+               cmd = CMD_INTERNAL_READ;
+               clockless = 1;
+       } else {
+               cmd = CMD_SINGLE_READ;
+               clockless = 0;
+       }
+
+       result = spi_cmd_complete(wilc, cmd, addr, (u8 *)data, 4, clockless);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr);
+               goto fail;
+       }
+
+       le32_to_cpus(data);
+
+fail:
+       if (result != N_OK) {
+               usleep_range(1000, 1100);
+               wilc_spi_reset(wilc);
+               dev_warn(&spi->dev, "Reset and retry %d %x\n", retry, addr);
+               usleep_range(1000, 1100);
+               retry--;
+               if (retry)
+                       goto retry;
+       }
+       return result;
+}
+
+static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int result;
+       u8 retry = SPI_RETRY_COUNT;
+
+       if (size <= 4)
+               return 0;
+
+retry:
+       result = spi_cmd_complete(wilc, CMD_DMA_EXT_READ, addr, buf, size, 0);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr);
+               goto fail;
+       }
+
+fail:
+       if (result != N_OK) {
+               usleep_range(1000, 1100);
+               wilc_spi_reset(wilc);
+               dev_warn(&spi->dev, "Reset and retry %d %x %d\n", retry, addr,
+                        size);
+               usleep_range(1000, 1100);
+               retry--;
+               if (retry)
+                       goto retry;
+       }
+       return result;
+}
+
+/********************************************
+ *
+ *      Bus interfaces
+ *
+ ********************************************/
+
+int wilc_spi_reset(struct wilc *wilc)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       int result = N_OK;
+
+       result = spi_cmd_complete(wilc, CMD_RESET, 0, 0, 0, 0);
+       if (result != N_OK) {
+               dev_err(&spi->dev, "Failed cmd reset\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+static bool wilc_spi_is_init(struct wilc *wilc)
+{
+       struct wilc_spi *spi_priv = wilc->bus_data;
+
+       return spi_priv->is_init;
+}
+
+static int wilc_spi_deinit(struct wilc *wilc)
+{
+       struct wilc_spi *spi_priv = wilc->bus_data;
+
+       /*
+        * TODO:
+        */
+       spi_priv->is_init = false;
+
+       return 1;
+}
+
+static int wilc_spi_init(struct wilc *wilc, bool resume)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       struct wilc_spi *spi_priv = wilc->bus_data;
+       u32 reg;
+       u32 chipid;
+
+       if (spi_priv->is_init) {
+               if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
+                       dev_err(&spi->dev, "Fail cmd read chip id...\n");
+                       return 0;
+               }
+               return 1;
+       }
+
+       /*
+        * configure protocol
+        */
+
+       /*
+        * TODO: We can remove the CRC trials if there is a definite
+        * way to reset
+        */
+       /* the SPI to it's initial value. */
+       if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg)) {
+               /*
+                * Read failed. Try with CRC off. This might happen when module
+                * is removed but chip isn't reset
+                */
+               spi_priv->crc_off = 1;
+               dev_err(&spi->dev,
+                       "Failed read with CRC on, retrying with CRC off\n");
+               if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg)) {
+                       /*
+                        * Read failed with both CRC on and off,
+                        * something went bad
+                        */
+                       dev_err(&spi->dev, "Failed internal read protocol...\n");
+                       return 0;
+               }
+       }
+       if (spi_priv->crc_off == 0) {
+               reg &= ~0xc; /* disable crc checking */
+               reg &= ~0x70;
+               reg |= (0x5 << 4);
+               if (!spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg)) {
+                       dev_err(&spi->dev,
+                               "[wilc spi %d]: Failed internal write reg\n",
+                               __LINE__);
+                       return 0;
+               }
+               spi_priv->crc_off = 1;
+       }
+
+       /*
+        * make sure can read back chip id correctly
+        */
+       if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
+               dev_err(&spi->dev, "Fail cmd read chip id...\n");
+               return 0;
+       }
+
+       if (!resume) {
+               chipid = wilc_get_chipid(wilc, true);
+               if (is_wilc3000(chipid)) {
+                       wilc->chip = WILC_3000;
+                       goto pass;
+               } else if (is_wilc1000(chipid)) {
+                       wilc->chip = WILC_1000;
+                       goto pass;
+               } else {
+                       dev_err(&spi->dev, "Unsupported chipid: %x\n", chipid);
+                       goto fail;
+               }
+               dev_dbg(&spi->dev, "chipid %08x\n", chipid);
+       }
+
+pass:
+       spi_priv->is_init = true;
+       return 1;
+
+fail:
+       return 0;
+}
+
+static int wilc_spi_read_size(struct wilc *wilc, u32 *size)
+{
+       int ret;
+
+       ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
+                               size);
+       *size = *size  & IRQ_DMA_WD_CNT_MASK;
+
+       return ret;
+}
+
+static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status)
+{
+       return spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
+                               int_status);
+}
+
+static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val)
+{
+       return spi_internal_write(wilc, 0xe844 - WILC_SPI_REG_BASE, val);
+}
+
+static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
+{
+       struct spi_device *spi = to_spi_device(wilc->dev);
+       struct wilc_spi *spi_priv = wilc->bus_data;
+       u32 reg;
+       int ret, i;
+
+       if (nint > MAX_NUM_INT) {
+               dev_err(&spi->dev, "Too many interrupts (%d)...\n", nint);
+               return 0;
+       }
+
+       spi_priv->nint = nint;
+
+       /*
+        * interrupt pin mux select
+        */
+       ret = wilc_spi_read_reg(wilc, WILC_PIN_MUX_0, &reg);
+       if (!ret) {
+               dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+                       WILC_PIN_MUX_0);
+               return 0;
+       }
+       reg |= BIT(8);
+       ret = wilc_spi_write_reg(wilc, WILC_PIN_MUX_0, reg);
+       if (!ret) {
+               dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+                       WILC_PIN_MUX_0);
+               return 0;
+       }
+
+       /*
+        * interrupt enable
+        */
+       ret = wilc_spi_read_reg(wilc, WILC_INTR_ENABLE, &reg);
+       if (!ret) {
+               dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+                       WILC_INTR_ENABLE);
+               return 0;
+       }
+
+       for (i = 0; (i < 5) && (nint > 0); i++, nint--)
+               reg |= (BIT((27 + i)));
+
+       ret = wilc_spi_write_reg(wilc, WILC_INTR_ENABLE, reg);
+       if (!ret) {
+               dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+                       WILC_INTR_ENABLE);
+               return 0;
+       }
+       if (nint) {
+               ret = wilc_spi_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
+               if (!ret) {
+                       dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+                               WILC_INTR2_ENABLE);
+                       return 0;
+               }
+
+               for (i = 0; (i < 3) && (nint > 0); i++, nint--)
+                       reg |= BIT(i);
+
+               ret = wilc_spi_write_reg(wilc, WILC_INTR2_ENABLE, reg);
+               if (!ret) {
+                       dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+                               WILC_INTR2_ENABLE);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/* Global spi HIF function table */
+static const struct wilc_hif_func wilc_hif_spi = {
+       .hif_init = wilc_spi_init,
+       .hif_deinit = wilc_spi_deinit,
+       .hif_read_reg = wilc_spi_read_reg,
+       .hif_write_reg = wilc_spi_write_reg,
+       .hif_block_rx = wilc_spi_read,
+       .hif_block_tx = wilc_spi_write,
+       .hif_read_int = wilc_spi_read_int,
+       .hif_clear_int_ext = wilc_spi_clear_int_ext,
+       .hif_read_size = wilc_spi_read_size,
+       .hif_block_tx_ext = wilc_spi_write,
+       .hif_block_rx_ext = wilc_spi_read,
+       .hif_sync_ext = wilc_spi_sync_ext,
+       .hif_reset = wilc_spi_reset,
+       .hif_is_init = wilc_spi_is_init,
+};
diff --git a/drivers/net/wireless/mchp/wilc_wfi_cfgoperations.c b/drivers/net/wireless/mchp/wilc_wfi_cfgoperations.c
new file mode 100644 (file)
index 0000000..4693ad9
--- /dev/null
@@ -0,0 +1,2405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "wilc_wfi_cfgoperations.h"
+#include "wilc_netdev.h"
+
+#define ACTION_CAT_ID                  24
+#define ACTION_SUBTYPE_ID              25
+#define P2P_PUB_ACTION_SUBTYPE         30
+
+#define ACTION_FRAME                   0xd0
+#define GO_INTENT_ATTR_ID              0x04
+#define CHANLIST_ATTR_ID               0x0b
+#define OPERCHAN_ATTR_ID               0x11
+#define PUB_ACTION_ATTR_ID             0x04
+#define P2PELEM_ATTR_ID                        0xdd
+
+#define GO_NEG_REQ                     0x00
+#define GO_NEG_RSP                     0x01
+#define GO_NEG_CONF                    0x02
+#define P2P_INV_REQ                    0x03
+#define P2P_INV_RSP                    0x04
+#define PUBLIC_ACT_VENDORSPEC          0x09
+#define GAS_INITIAL_REQ                        0x0a
+#define GAS_INITIAL_RSP                        0x0b
+
+#define WILC_INVALID_CHANNEL           0
+
+static const struct ieee80211_txrx_stypes
+       wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = {
+       [NL80211_IFTYPE_STATION] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                       BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_AP] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                       BIT(IEEE80211_STYPE_AUTH >> 4) |
+                       BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+                       BIT(IEEE80211_STYPE_ACTION >> 4)
+       },
+       [NL80211_IFTYPE_P2P_CLIENT] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                       BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                       BIT(IEEE80211_STYPE_AUTH >> 4) |
+                       BIT(IEEE80211_STYPE_DEAUTH >> 4)
+       }
+};
+
+static const struct wiphy_wowlan_support wowlan_support = {
+       .flags = WIPHY_WOWLAN_ANY
+};
+
+struct wilc_p2p_mgmt_data {
+       int size;
+       u8 *buff;
+};
+
+static const u8 p2p_oui[] = {0x50, 0x6f, 0x9A, 0x09};
+static const u8 p2p_vendor_spec[] = {0xdd, 0x05, 0x00, 0x08, 0x40, 0x03};
+
+static void cfg_scan_result(enum scan_event scan_event,
+                           struct wilc_rcvd_net_info *info, void *user_void)
+{
+       struct wilc_priv *priv = user_void;
+
+       if (!priv->cfg_scanning)
+               return;
+
+       if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
+               s32 freq;
+               struct ieee80211_channel *channel;
+               struct cfg80211_bss *bss;
+               struct wiphy *wiphy = priv->dev->ieee80211_ptr->wiphy;
+
+               if (!wiphy || !info)
+                       return;
+
+               freq = ieee80211_channel_to_frequency((s32)info->ch,
+                                                     NL80211_BAND_2GHZ);
+               channel = ieee80211_get_channel(wiphy, freq);
+               if (!channel)
+                       return;
+
+               PRINT_D(priv->dev, CFG80211_DBG,
+                       "Network Info:: CHANNEL Frequency: %d, RSSI: %d,\n",
+                       freq, ((s32)info->rssi * 100));
+
+               bss = cfg80211_inform_bss_frame(wiphy, channel, info->mgmt,
+                                               info->frame_len,
+                                               (s32)info->rssi * 100,
+                                               GFP_KERNEL);
+               if (!bss)
+                       cfg80211_put_bss(wiphy, bss);
+       } else if (scan_event == SCAN_EVENT_DONE) {
+               PRINT_INFO(priv->dev, CFG80211_DBG, "Scan Done[%p]\n",
+                          priv->dev);
+               mutex_lock(&priv->scan_req_lock);
+
+               if (priv->scan_req) {
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+                       struct cfg80211_scan_info info = {
+                               .aborted = false,
+                       };
+
+                       cfg80211_scan_done(priv->scan_req, &info);
+#else
+                       cfg80211_scan_done(priv->scan_req, false);
+#endif
+                       priv->cfg_scanning = false;
+                       priv->scan_req = NULL;
+               }
+               mutex_unlock(&priv->scan_req_lock);
+       } else if (scan_event == SCAN_EVENT_ABORTED) {
+               mutex_lock(&priv->scan_req_lock);
+
+               PRINT_INFO(priv->dev, CFG80211_DBG, "Scan Aborted\n");
+               if (priv->scan_req) {
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+                       struct cfg80211_scan_info info = {
+                               .aborted = false,
+                       };
+                       cfg80211_scan_done(priv->scan_req, &info);
+#else
+                       cfg80211_scan_done(priv->scan_req, false);
+#endif
+
+                       priv->cfg_scanning = false;
+                       priv->scan_req = NULL;
+               }
+               mutex_unlock(&priv->scan_req_lock);
+       }
+}
+
+static void cfg_connect_result(enum conn_event conn_disconn_evt,
+                              u8 mac_status, void *priv_data)
+{
+       struct wilc_priv *priv = priv_data;
+       struct net_device *dev = priv->dev;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct host_if_drv *wfi_drv = priv->hif_drv;
+       struct wilc_conn_info *conn_info = &wfi_drv->conn_info;
+
+       vif->connecting = false;
+
+       if (conn_disconn_evt == EVENT_CONN_RESP) {
+               u16 connect_status = conn_info->status;
+
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Connection response received=%d connect_stat[%d]\n",
+                          mac_status, connect_status);
+               if (mac_status == WILC_MAC_STATUS_DISCONNECTED &&
+                   connect_status == WLAN_STATUS_SUCCESS) {
+                       connect_status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+                       if (vif->iftype != WILC_CLIENT_MODE)
+                               vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
+
+                       PRINT_ER(dev, "Unspecified failure\n");
+               }
+
+               if (connect_status == WLAN_STATUS_SUCCESS) {
+                       PRINT_ER(dev,
+                               "Connection Successful: BSSID: %x%x%x%x%x%x\n",
+                               conn_info->bssid[0], conn_info->bssid[1],
+                               conn_info->bssid[2], conn_info->bssid[3],
+                               conn_info->bssid[4], conn_info->bssid[5]);
+                       memcpy(priv->associated_bss, conn_info->bssid,
+                              ETH_ALEN);
+               }
+
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Association request info elements length = %d\n",
+                          conn_info->req_ies_len);
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Association response info elements length = %d\n",
+                          conn_info->resp_ies_len);
+               cfg80211_connect_result(dev, conn_info->bssid,
+                                       conn_info->req_ies,
+                                       conn_info->req_ies_len,
+                                       conn_info->resp_ies,
+                                       conn_info->resp_ies_len, connect_status,
+                                       GFP_KERNEL);
+       } else if (conn_disconn_evt == EVENT_DISCONN_NOTIF) {
+               u16 reason = 0;
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+               vif->obtaining_ip = false;
+#endif
+               PRINT_ER(vif->ndev,
+                        "Received WILC_MAC_STATUS_DISCONNECTED dev [%p]\n",
+                        priv->dev);
+               priv->p2p.local_random = 0x01;
+               priv->p2p.recv_random = 0x00;
+               priv->p2p.is_wilc_ie = false;
+               eth_zero_addr(priv->associated_bss);
+               wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+               if (vif->iftype != WILC_CLIENT_MODE) {
+                       vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
+               } else {
+                       if (wfi_drv->ifc_up)
+                               reason = 3;
+                       else
+                               reason = 1;
+               }
+#if KERNEL_VERSION(4, 2, 0) > LINUX_VERSION_CODE
+               cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL);
+#else
+               cfg80211_disconnected(dev, reason, NULL, 0, false, GFP_KERNEL);
+#endif
+       }
+}
+
+static int set_channel(struct wiphy *wiphy,
+                      struct cfg80211_chan_def *chandef)
+{
+       u32 channelnum = 0;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       int result = 0;
+
+       channelnum = ieee80211_frequency_to_channel(chandef->chan->center_freq);
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Setting channel %d with frequency %d\n",
+                  channelnum, chandef->chan->center_freq);
+
+       vif->wilc->op_ch = channelnum;
+       result = wilc_set_mac_chnl_num(vif, channelnum);
+
+       if (result != 0)
+               PRINT_ER(priv->dev, "Error in setting channel %d\n",
+                        channelnum);
+
+       return result;
+}
+
+static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       u32 i;
+       int ret = 0;
+       u8 scan_ch_list[WILC_MAX_NUM_SCANNED_CH];
+       u8 scan_type;
+
+       if (request->n_channels > WILC_MAX_NUM_SCANNED_CH) {
+               PRINT_ER(priv->dev, "Requested scanned channels over\n");
+               return -EINVAL;
+       }
+
+       priv->scan_req = request;
+       priv->cfg_scanning = true;
+       for (i = 0; i < request->n_channels; i++) {
+               u16 freq = request->channels[i]->center_freq;
+
+               scan_ch_list[i] = (u8)ieee80211_frequency_to_channel(freq);
+               PRINT_D(vif->ndev, CFG80211_DBG,
+                       "ScanChannel List[%d] = %d",
+                       i, scan_ch_list[i]);
+       }
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Requested num of channel %d\n",
+                  request->n_channels);
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Scan Request IE len =  %d\n",
+                  request->ie_len);
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Number of SSIDs %d\n",
+                  request->n_ssids);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Trigger Scan Request\n");
+
+       if (request->n_ssids)
+               scan_type = WILC_FW_ACTIVE_SCAN;
+       else
+               scan_type = WILC_FW_PASSIVE_SCAN;
+
+       ret = wilc_scan(vif, WILC_FW_USER_SCAN, scan_type, scan_ch_list,
+                       request->n_channels, cfg_scan_result, (void *)priv,
+                       request);
+
+       if (ret) {
+               priv->scan_req = NULL;
+               priv->cfg_scanning = false;
+               PRINT_WRN(vif->ndev, CFG80211_DBG,
+                         "Device is busy: Error(%d)\n", ret);
+       }
+
+       return ret;
+}
+
+static int connect(struct wiphy *wiphy, struct net_device *dev,
+                  struct cfg80211_connect_params *sme)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       struct host_if_drv *wfi_drv = priv->hif_drv;
+       int ret;
+       u32 i;
+       u8 security = WILC_FW_SEC_NO;
+       enum authtype auth_type = WILC_FW_AUTH_ANY;
+       u32 cipher_group;
+       struct cfg80211_bss *bss;
+       void *join_params;
+       u8 ch;
+
+       vif->connecting = true;
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Connecting to SSID [%s] on netdev [%p] host if [%x]\n",
+                  sme->ssid, dev, (u32)priv->hif_drv);
+
+       if (vif->iftype == WILC_CLIENT_MODE)
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Connected to Direct network,OBSS disabled\n");
+
+       PRINT_D(vif->ndev, CFG80211_DBG, "Required SSID= %s\n, AuthType= %d\n",
+               sme->ssid, sme->auth_type);
+
+       memset(priv->wep_key, 0, sizeof(priv->wep_key));
+       memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
+
+       PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.wpa_versions=%x\n",
+               sme->crypto.wpa_versions);
+       PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.cipher_group=%x\n",
+               sme->crypto.cipher_group);
+       PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.n_ciphers_pairwise=%d\n",
+               sme->crypto.n_ciphers_pairwise);
+       for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++)
+               PRINT_D(vif->ndev, CORECONFIG_DBG,
+                       "sme->crypto.ciphers_pairwise[%d]=%x\n", i,
+                       sme->crypto.ciphers_pairwise[i]);
+
+       cipher_group = sme->crypto.cipher_group;
+       if (cipher_group != 0) {
+               PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+                          ">> sme->crypto.wpa_versions: %x\n",
+                          sme->crypto.wpa_versions);
+               if (cipher_group == WLAN_CIPHER_SUITE_WEP40) {
+                       security = WILC_FW_SEC_WEP;
+                       PRINT_D(vif->ndev, CFG80211_DBG,
+                               "WEP Default Key Idx = %d\n", sme->key_idx);
+
+                       for (i = 0; i < sme->key_len; i++)
+                               PRINT_D(vif->ndev, CORECONFIG_DBG,
+                               "WEP Key Value[%d] = %d\n", i, sme->key[i]);
+
+                       priv->wep_key_len[sme->key_idx] = sme->key_len;
+                       memcpy(priv->wep_key[sme->key_idx], sme->key,
+                              sme->key_len);
+
+                       wilc_set_wep_default_keyid(vif, sme->key_idx);
+                       wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+                                                sme->key_idx);
+               } else if (cipher_group == WLAN_CIPHER_SUITE_WEP104) {
+                       security = WILC_FW_SEC_WEP_EXTENDED;
+
+                       priv->wep_key_len[sme->key_idx] = sme->key_len;
+                       memcpy(priv->wep_key[sme->key_idx], sme->key,
+                              sme->key_len);
+
+                       wilc_set_wep_default_keyid(vif, sme->key_idx);
+                       wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+                                                sme->key_idx);
+               } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
+                       if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
+                               security = WILC_FW_SEC_WPA2_TKIP;
+                       else
+                               security = WILC_FW_SEC_WPA2_AES;
+               } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
+                       if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
+                               security = WILC_FW_SEC_WPA_TKIP;
+                       else
+                               security = WILC_FW_SEC_WPA_AES;
+               } else {
+                       ret = -ENOTSUPP;
+                       PRINT_ER(dev, "Unsupported cipher\n");
+                       goto out_error;
+               }
+       }
+
+       if ((sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) ||
+           (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) {
+               for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) {
+                       u32 ciphers_pairwise = sme->crypto.ciphers_pairwise[i];
+
+                       if (ciphers_pairwise == WLAN_CIPHER_SUITE_TKIP)
+                               security |= WILC_FW_TKIP;
+                       else
+                               security |= WILC_FW_AES;
+               }
+       }
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Adding key with cipher group %x\n",
+                  cipher_group);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Authentication Type = %d\n",
+                  sme->auth_type);
+       switch (sme->auth_type) {
+       case NL80211_AUTHTYPE_OPEN_SYSTEM:
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "In OPEN SYSTEM\n");
+               auth_type = WILC_FW_AUTH_OPEN_SYSTEM;
+               break;
+
+       case NL80211_AUTHTYPE_SHARED_KEY:
+               auth_type = WILC_FW_AUTH_SHARED_KEY;
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "In SHARED KEY\n");
+               break;
+
+       default:
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Automatic Authentication type= %d\n",
+                          sme->auth_type);
+               break;
+       }
+
+       if (sme->crypto.n_akm_suites) {
+               if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X)
+                       auth_type = WILC_FW_AUTH_IEEE8021;
+       }
+
+       if (wfi_drv->usr_scan_req.scan_result) {
+               netdev_err(vif->ndev, "%s: Scan in progress\n", __func__);
+               ret = -EBUSY;
+               goto out_error;
+       }
+
+#if KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE
+       bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid,
+                              sme->ssid_len, WLAN_CAPABILITY_ESS,
+                              WLAN_CAPABILITY_ESS);
+#else
+       bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid,
+                              sme->ssid_len, IEEE80211_BSS_TYPE_ANY,
+                              IEEE80211_PRIVACY(sme->privacy));
+#endif
+       if (!bss) {
+               ret = -EINVAL;
+               goto out_error;
+       }
+
+       if (ether_addr_equal_unaligned(vif->bssid, bss->bssid)) {
+               ret = -EALREADY;
+               goto out_put_bss;
+       }
+
+       join_params = wilc_parse_join_bss_param(bss, &sme->crypto);
+       if (!join_params) {
+               PRINT_ER(vif->ndev, "%s: failed to construct join param\n",
+                        __func__);
+               ret = -EINVAL;
+               goto out_put_bss;
+       }
+
+       ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+       PRINT_D(vif->ndev, CFG80211_DBG, "Required Channel = %d\n", ch);
+       vif->wilc->op_ch = ch;
+       if (vif->iftype != WILC_CLIENT_MODE)
+               vif->wilc->sta_ch = ch;
+
+       wilc_wlan_set_bssid(dev, bss->bssid, WILC_STATION_MODE);
+
+       wfi_drv->conn_info.security = security;
+       wfi_drv->conn_info.auth_type = auth_type;
+       wfi_drv->conn_info.ch = ch;
+       wfi_drv->conn_info.conn_result = cfg_connect_result;
+       wfi_drv->conn_info.arg = priv;
+       wfi_drv->conn_info.param = join_params;
+
+       ret = wilc_set_join_req(vif, bss->bssid, sme->ie, sme->ie_len);
+       if (ret) {
+               PRINT_ER(dev, "wilc_set_join_req(): Error(%d)\n", ret);
+               ret = -ENOENT;
+               if (vif->iftype != WILC_CLIENT_MODE)
+                       vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
+               wilc_wlan_set_bssid(dev, NULL, WILC_STATION_MODE);
+               wfi_drv->conn_info.conn_result = NULL;
+               kfree(join_params);
+               goto out_put_bss;
+       }
+       kfree(join_params);
+       cfg80211_put_bss(wiphy, bss);
+       return 0;
+
+out_put_bss:
+       cfg80211_put_bss(wiphy, bss);
+
+out_error:
+       vif->connecting = false;
+       return ret;
+}
+
+static int disconnect(struct wiphy *wiphy, struct net_device *dev,
+                     u16 reason_code)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       struct wilc *wilc = vif->wilc;
+       struct host_if_drv *wfi_drv;
+       int ret;
+
+       vif->connecting = false;
+
+       if (!wilc)
+               return -EIO;
+       wfi_drv = (struct host_if_drv *)priv->hif_drv;
+       if (vif->iftype != WILC_CLIENT_MODE)
+               wilc->sta_ch = WILC_INVALID_CHANNEL;
+       wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Disconnecting with reason code(%d)\n", reason_code);
+       priv->p2p.local_random = 0x01;
+       priv->p2p.recv_random = 0x00;
+       priv->p2p.is_wilc_ie = false;
+       wfi_drv->p2p_timeout = 0;
+
+       ret = wilc_disconnect(vif);
+       if (ret != 0) {
+               PRINT_ER(priv->dev, "Error in disconnecting (%d)\n", ret);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static inline void wilc_wfi_cfg_copy_wep_info(struct wilc_priv *priv,
+                                             u8 key_index,
+                                             struct key_params *params)
+{
+       priv->wep_key_len[key_index] = params->key_len;
+       memcpy(priv->wep_key[key_index], params->key, params->key_len);
+}
+
+static int wilc_wfi_cfg_allocate_wpa_entry(struct wilc_priv *priv, u8 idx)
+{
+       if (!priv->wilc_gtk[idx]) {
+               priv->wilc_gtk[idx] = kzalloc(sizeof(*priv->wilc_gtk[idx]),
+                                             GFP_KERNEL);
+               if (!priv->wilc_gtk[idx])
+                       return -ENOMEM;
+       }
+
+       if (!priv->wilc_ptk[idx]) {
+               priv->wilc_ptk[idx] = kzalloc(sizeof(*priv->wilc_ptk[idx]),
+                                             GFP_KERNEL);
+               if (!priv->wilc_ptk[idx])
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int wilc_wfi_cfg_copy_wpa_info(struct wilc_wfi_key *key_info,
+                                     struct key_params *params)
+{
+       kfree(key_info->key);
+
+       key_info->key = kmemdup(params->key, params->key_len, GFP_KERNEL);
+       if (!key_info->key)
+               return -ENOMEM;
+
+       kfree(key_info->seq);
+
+       if (params->seq_len > 0) {
+               key_info->seq = kmemdup(params->seq, params->seq_len,
+                                       GFP_KERNEL);
+               if (!key_info->seq)
+                       return -ENOMEM;
+       }
+
+       key_info->cipher = params->cipher;
+       key_info->key_len = params->key_len;
+       key_info->seq_len = params->seq_len;
+
+       return 0;
+}
+
+static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+                  bool pairwise,
+                  const u8 *mac_addr, struct key_params *params)
+
+{
+       int ret = 0, keylen = params->key_len;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       const u8 *rx_mic = NULL;
+       const u8 *tx_mic = NULL;
+       u8 mode = WILC_FW_SEC_NO;
+       u8 op_mode;
+       int i;
+       struct wilc_vif *vif = netdev_priv(netdev);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Adding key with cipher suite = %x\n", params->cipher);
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "%x %x %d\n", (u32)wiphy,
+                  (u32)netdev, key_index);
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "key %x %x %x\n", params->key[0],
+                  params->key[1],
+                  params->key[2]);
+       switch (params->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               if (priv->wdev->iftype == NL80211_IFTYPE_AP) {
+                       wilc_wfi_cfg_copy_wep_info(priv, key_index, params);
+
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Adding AP WEP Default key Idx = %d\n",
+                                  key_index);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Adding AP WEP Key len= %d\n",
+                                  params->key_len);
+
+                       for (i = 0; i < params->key_len; i++)
+                               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                          "WEP AP key val[%d] = %x\n", i,
+                                          params->key[i]);
+
+                       if (params->cipher == WLAN_CIPHER_SUITE_WEP40)
+                               mode = WILC_FW_SEC_WEP;
+                       else
+                               mode = WILC_FW_SEC_WEP_EXTENDED;
+
+                       ret = wilc_add_wep_key_bss_ap(vif, params->key,
+                                                     params->key_len,
+                                                     key_index, mode,
+                                                     WILC_FW_AUTH_OPEN_SYSTEM);
+                       break;
+               }
+               if (memcmp(params->key, priv->wep_key[key_index],
+                          params->key_len)) {
+                       wilc_wfi_cfg_copy_wep_info(priv, key_index, params);
+
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Adding WEP Default key Idx = %d\n",
+                                  key_index);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Adding WEP Key length = %d\n",
+                                  params->key_len);
+                       ret = wilc_add_wep_key_bss_sta(vif, params->key,
+                                                      params->key_len,
+                                                      key_index);
+               }
+
+               break;
+
+       case WLAN_CIPHER_SUITE_TKIP:
+       case WLAN_CIPHER_SUITE_CCMP:
+               if (priv->wdev->iftype == NL80211_IFTYPE_AP ||
+                   priv->wdev->iftype == NL80211_IFTYPE_P2P_GO) {
+                       struct wilc_wfi_key *key;
+
+                       ret = wilc_wfi_cfg_allocate_wpa_entry(priv, key_index);
+                       if (ret)
+                               return -ENOMEM;
+
+                       if (params->key_len > 16 &&
+                           params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+                               tx_mic = params->key + 24;
+                               rx_mic = params->key + 16;
+                               keylen = params->key_len - 16;
+                       }
+
+                       if (!pairwise) {
+                               if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+                                       mode = WILC_FW_SEC_WPA_TKIP;
+                               else
+                                       mode = WILC_FW_SEC_WPA2_AES;
+
+                               priv->wilc_groupkey = mode;
+
+                               key = priv->wilc_gtk[key_index];
+                       } else {
+                               PRINT_D(vif->ndev, CFG80211_DBG,
+                                       "STA Address: %x%x%x%x%x\n",
+                                       mac_addr[0], mac_addr[1], mac_addr[2],
+                                       mac_addr[3], mac_addr[4]);
+                               if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+                                       mode = WILC_FW_SEC_WPA_TKIP;
+                               else
+                                       mode = (priv->wilc_groupkey |
+                                               WILC_FW_AES);
+
+                               key = priv->wilc_ptk[key_index];
+                       }
+                       ret = wilc_wfi_cfg_copy_wpa_info(key, params);
+                       if (ret)
+                               return -ENOMEM;
+
+                       op_mode = WILC_AP_MODE;
+               } else {
+                       if (params->key_len > 16 &&
+                           params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+                               rx_mic = params->key + 24;
+                               tx_mic = params->key + 16;
+                               keylen = params->key_len - 16;
+                       }
+
+                       op_mode = WILC_STATION_MODE;
+               }
+
+               if (!pairwise)
+                       ret = wilc_add_rx_gtk(vif, params->key, keylen,
+                                             key_index, params->seq_len,
+                                             params->seq, rx_mic, tx_mic,
+                                             op_mode, mode);
+               else
+                       ret = wilc_add_ptk(vif, params->key, keylen, mac_addr,
+                                          rx_mic, tx_mic, op_mode, mode,
+                                          key_index);
+
+               break;
+
+       default:
+               PRINT_ER(netdev, "Unsupported cipher\n");
+               ret = -ENOTSUPP;
+       }
+
+       return ret;
+}
+
+static int del_key(struct wiphy *wiphy, struct net_device *netdev,
+                  u8 key_index,
+                  bool pairwise,
+                  const u8 *mac_addr)
+{
+       int ret = 0;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(netdev);
+       struct wilc *wl = vif->wilc;
+
+       if (netdev == wl->vif[0]->ndev) {
+               if (priv->wilc_gtk[key_index]) {
+                       kfree(priv->wilc_gtk[key_index]->key);
+                       priv->wilc_gtk[key_index]->key = NULL;
+                       kfree(priv->wilc_gtk[key_index]->seq);
+                       priv->wilc_gtk[key_index]->seq = NULL;
+
+                       kfree(priv->wilc_gtk[key_index]);
+                       priv->wilc_gtk[key_index] = NULL;
+               }
+
+               if (priv->wilc_ptk[key_index]) {
+                       kfree(priv->wilc_ptk[key_index]->key);
+                       priv->wilc_ptk[key_index]->key = NULL;
+                       kfree(priv->wilc_ptk[key_index]->seq);
+                       priv->wilc_ptk[key_index]->seq = NULL;
+                       kfree(priv->wilc_ptk[key_index]);
+                       priv->wilc_ptk[key_index] = NULL;
+               }
+       }
+
+       if (key_index <= 3 && priv->wep_key_len[key_index]) {
+               memset(priv->wep_key[key_index], 0,
+                      priv->wep_key_len[key_index]);
+               priv->wep_key_len[key_index] = 0;
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Removing WEP key with index = %d\n",
+                          key_index);
+               ret = wilc_remove_wep_key(vif, key_index);
+       }
+
+       return ret;
+}
+
+static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+                  bool pairwise, const u8 *mac_addr, void *cookie,
+                  void (*callback)(void *cookie, struct key_params *))
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct  key_params key_params;
+       struct wilc_vif *vif = netdev_priv(netdev);
+
+       if (!pairwise) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Getting group key idx: %x\n", key_index);
+               key_params.key = priv->wilc_gtk[key_index]->key;
+               key_params.cipher = priv->wilc_gtk[key_index]->cipher;
+               key_params.key_len = priv->wilc_gtk[key_index]->key_len;
+               key_params.seq = priv->wilc_gtk[key_index]->seq;
+               key_params.seq_len = priv->wilc_gtk[key_index]->seq_len;
+       } else {
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "Getting pairwise key\n");
+               key_params.key = priv->wilc_ptk[key_index]->key;
+               key_params.cipher = priv->wilc_ptk[key_index]->cipher;
+               key_params.key_len = priv->wilc_ptk[key_index]->key_len;
+               key_params.seq = priv->wilc_ptk[key_index]->seq;
+               key_params.seq_len = priv->wilc_ptk[key_index]->seq_len;
+       }
+
+       callback(cookie, &key_params);
+
+       return 0;
+}
+
+static int set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+                          u8 key_index, bool unicast, bool multicast)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       wilc_set_wep_default_keyid(vif, key_index);
+
+       return 0;
+}
+
+#if KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int get_station(struct wiphy *wiphy, struct net_device *dev,
+                      const u8 *mac, struct station_info *sinfo)
+#else
+static int get_station(struct wiphy *wiphy, struct net_device *dev,
+                      u8 *mac, struct station_info *sinfo)
+#endif
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       u32 i = 0;
+       u32 associatedsta = ~0;
+       u32 inactive_time = 0;
+
+       if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Getting station parameters\n");
+               for (i = 0; i < NUM_STA_ASSOCIATED; i++) {
+                       if (!(memcmp(mac,
+                                    priv->assoc_stainfo.sta_associated_bss[i],
+                                    ETH_ALEN))) {
+                               associatedsta = i;
+                               break;
+                       }
+               }
+
+               if (associatedsta == ~0) {
+                       PRINT_ER(dev, "sta required is not associated\n");
+                       return -ENOENT;
+               }
+
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+               sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME);
+#else
+               sinfo->filled |= STATION_INFO_INACTIVE_TIME;
+#endif
+
+               wilc_get_inactive_time(vif, mac, &inactive_time);
+               sinfo->inactive_time = 1000 * inactive_time;
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "Inactive time %d\n",
+                          sinfo->inactive_time);
+       } else if (vif->iftype == WILC_STATION_MODE) {
+               struct rf_info stats;
+
+               if (!wilc->initialized) {
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "driver not initialized\n");
+                       return -EBUSY;
+               }
+               wilc_get_statistics(vif, &stats);
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+               sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL) |
+                             BIT(NL80211_STA_INFO_RX_PACKETS) |
+                             BIT(NL80211_STA_INFO_TX_PACKETS) |
+                             BIT(NL80211_STA_INFO_TX_FAILED) |
+                             BIT(NL80211_STA_INFO_TX_BITRATE);
+#else
+               sinfo->filled |= STATION_INFO_SIGNAL |
+                             STATION_INFO_RX_PACKETS |
+                             STATION_INFO_TX_PACKETS |
+                             STATION_INFO_TX_FAILED |
+                             STATION_INFO_TX_BITRATE;
+#endif
+               sinfo->signal = stats.rssi;
+               sinfo->rx_packets = stats.rx_cnt;
+               sinfo->tx_packets = stats.tx_cnt + stats.tx_fail_cnt;
+               sinfo->tx_failed = stats.tx_fail_cnt;
+               sinfo->txrate.legacy = stats.link_speed * 10;
+
+               if (stats.link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
+                   stats.link_speed != DEFAULT_LINK_SPEED)
+                       wilc_enable_tcp_ack_filter(vif, true);
+               else if (stats.link_speed != DEFAULT_LINK_SPEED)
+                       wilc_enable_tcp_ack_filter(vif, false);
+
+               PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+                          "*** stats[%d][%d][%d][%d][%d]\n", sinfo->signal,
+                          sinfo->rx_packets, sinfo->tx_packets,
+                          sinfo->tx_failed, sinfo->txrate.legacy);
+       }
+       return 0;
+}
+
+static int change_bss(struct wiphy *wiphy, struct net_device *dev,
+                     struct bss_parameters *params)
+{
+       PRINT_INFO(dev, CFG80211_DBG, "Changing Bss parametrs\n");
+       return 0;
+}
+
+static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+       int ret;
+       struct cfg_param_attr cfg_param_val;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       cfg_param_val.flag = 0;
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting Wiphy params\n");
+
+       if (changed & WIPHY_PARAM_RETRY_SHORT) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Setting WIPHY_PARAM_RETRY_SHORT %d\n",
+                          wiphy->retry_short);
+               cfg_param_val.flag  |= WILC_CFG_PARAM_RETRY_SHORT;
+               cfg_param_val.short_retry_limit = wiphy->retry_short;
+       }
+       if (changed & WIPHY_PARAM_RETRY_LONG) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Setting WIPHY_PARAM_RETRY_LONG %d\n",
+                          wiphy->retry_long);
+               cfg_param_val.flag |= WILC_CFG_PARAM_RETRY_LONG;
+               cfg_param_val.long_retry_limit = wiphy->retry_long;
+       }
+       if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+               if (wiphy->frag_threshold > 255 &&
+                   wiphy->frag_threshold < 7937) {
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Setting WIPHY_PARAM_FRAG_THRESHOLD %d\n",
+                                  wiphy->frag_threshold);
+                       cfg_param_val.flag |= WILC_CFG_PARAM_RETRY_LONG;
+                       cfg_param_val.frag_threshold = wiphy->frag_threshold;
+               } else {
+                       PRINT_ER(vif->ndev,
+                                "Fragmentation threshold out of range\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+               if (wiphy->rts_threshold > 255) {
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Setting WIPHY_PARAM_RTS_THRESHOLD %d\n",
+                                  wiphy->rts_threshold);
+                       cfg_param_val.flag |= WILC_CFG_PARAM_RTS_THRESHOLD;
+                       cfg_param_val.rts_threshold = wiphy->rts_threshold;
+               } else {
+                       PRINT_ER(vif->ndev, "RTS threshold out of range\n");
+                       return -EINVAL;
+               }
+       }
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Setting CFG params in the host interface\n");
+       ret = wilc_hif_set_cfg(vif, &cfg_param_val);
+       if (ret)
+               PRINT_ER(priv->dev, "Error in setting WIPHY PARAMS\n");
+
+       return ret;
+}
+
+static int set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+                    struct cfg80211_pmksa *pmksa)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       u32 i;
+       int ret = 0;
+       u8 flag = 0;
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting PMKSA\n");
+
+       for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
+               if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
+                           ETH_ALEN)) {
+                       flag = PMKID_FOUND;
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "PMKID already exists\n");
+                       break;
+               }
+       }
+       if (i < WILC_MAX_NUM_PMKIDS) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Setting PMKID in private structure\n");
+               memcpy(priv->pmkid_list.pmkidlist[i].bssid, pmksa->bssid,
+                      ETH_ALEN);
+               memcpy(priv->pmkid_list.pmkidlist[i].pmkid, pmksa->pmkid,
+                      WLAN_PMKID_LEN);
+               if (!(flag == PMKID_FOUND))
+                       priv->pmkid_list.numpmkid++;
+       } else {
+               PRINT_ER(netdev, "Invalid PMKID index\n");
+               ret = -EINVAL;
+       }
+
+       if (!ret) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Setting pmkid in the host interface\n");
+               ret = wilc_set_pmkid_info(vif, &priv->pmkid_list);
+       }
+       return ret;
+}
+
+static int del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+                    struct cfg80211_pmksa *pmksa)
+{
+       u32 i;
+       int ret = 0;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+
+       PRINT_INFO(netdev, CFG80211_DBG, "Deleting PMKSA keys\n");
+
+       for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
+               if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
+                           ETH_ALEN)) {
+                       PRINT_INFO(netdev, CFG80211_DBG,
+                                  "Reseting PMKID values\n");
+                       memset(&priv->pmkid_list.pmkidlist[i], 0,
+                              sizeof(struct wilc_pmkid));
+                       break;
+               }
+       }
+
+       if (i < priv->pmkid_list.numpmkid && priv->pmkid_list.numpmkid > 0) {
+               for (; i < (priv->pmkid_list.numpmkid - 1); i++) {
+                       memcpy(priv->pmkid_list.pmkidlist[i].bssid,
+                              priv->pmkid_list.pmkidlist[i + 1].bssid,
+                              ETH_ALEN);
+                       memcpy(priv->pmkid_list.pmkidlist[i].pmkid,
+                              priv->pmkid_list.pmkidlist[i + 1].pmkid,
+                              WLAN_PMKID_LEN);
+               }
+               priv->pmkid_list.numpmkid--;
+       } else {
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+
+       PRINT_INFO(netdev, CFG80211_DBG, "Flushing  PMKID key values\n");
+       memset(&priv->pmkid_list, 0, sizeof(struct wilc_pmkid_attr));
+
+       return 0;
+}
+
+static inline void wilc_wfi_cfg_parse_ch_attr(struct wilc_vif *vif, u8 *buf,
+                                             u8 ch_list_attr_idx,
+                                             u8 op_ch_attr_idx, u8 sta_ch)
+{
+       int i = 0;
+       int j = 0;
+
+       if (ch_list_attr_idx) {
+               u8 limit = ch_list_attr_idx + 3 + buf[ch_list_attr_idx + 1];
+
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Modify channel list attribute [%d]\n",
+                          sta_ch);
+               for (i = ch_list_attr_idx + 3; i < limit; i++) {
+                       if (buf[i] == 0x51) {
+                               for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++)
+                                       buf[j] = sta_ch;
+                               break;
+                       }
+               }
+       }
+
+       if (op_ch_attr_idx) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Modify operating channel attribute %d\n",
+                          sta_ch);
+               buf[op_ch_attr_idx + 6] = 0x51;
+               buf[op_ch_attr_idx + 7] = sta_ch;
+       }
+}
+
+static void wilc_wfi_cfg_parse_rx_action(struct wilc_vif *vif, u8 *buf,
+                                        u32 len, u8 sta_ch, bool p2p_mode)
+{
+       u32 index = 0;
+       u8 op_channel_attr_index = 0;
+       u8 channel_list_attr_index = 0;
+
+       while (index < len) {
+               if (buf[index] == GO_INTENT_ATTR_ID) {
+                       if (!p2p_mode)
+                               buf[index + 3] = (buf[index + 3]  & 0x01) |
+                                                (0x0f << 1);
+                       else
+                               buf[index + 3] = (buf[index + 3]  & 0x01) |
+                                                (0x00 << 1);
+               }
+               if (buf[index] ==  CHANLIST_ATTR_ID)
+                       channel_list_attr_index = index;
+               else if (buf[index] ==  OPERCHAN_ATTR_ID)
+                       op_channel_attr_index = index;
+               index += buf[index + 1] + 3;
+       }
+       if (sta_ch != WILC_INVALID_CHANNEL)
+               wilc_wfi_cfg_parse_ch_attr(vif, buf, channel_list_attr_index,
+                                          op_channel_attr_index, sta_ch);
+}
+
+static void wilc_wfi_cfg_parse_tx_action(struct wilc_vif *vif, u8 *buf,
+                                        u32 len, bool oper_ch, u8 sta_ch,
+                                        u8 p2p_mode)
+{
+       u32 index = 0;
+       u8 op_channel_attr_index = 0;
+       u8 channel_list_attr_index = 0;
+
+       while (index < len) {
+               if (buf[index] == GO_INTENT_ATTR_ID) {
+                       if (!p2p_mode)
+                               buf[index + 3] = (buf[index + 3]  & 0x01) |
+                                                (0x00 << 1);
+                       else
+                               buf[index + 3] = (buf[index + 3]  & 0x01) |
+                                                (0x0f << 1);
+                       break;
+               }
+
+               if (buf[index] ==  CHANLIST_ATTR_ID)
+                       channel_list_attr_index = index;
+               else if (buf[index] ==  OPERCHAN_ATTR_ID)
+                       op_channel_attr_index = index;
+               index += buf[index + 1] + 3;
+       }
+       if (sta_ch != WILC_INVALID_CHANNEL && oper_ch)
+               wilc_wfi_cfg_parse_ch_attr(vif, buf, channel_list_attr_index,
+                                          op_channel_attr_index, sta_ch);
+}
+
+static void wilc_wfi_cfg_parse_rx_vendor_spec(struct wilc_priv *priv, u8 *buff,
+                                             u32 size)
+{
+       int i;
+       u8 subtype;
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       subtype = buff[P2P_PUB_ACTION_SUBTYPE];
+       if ((subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) &&
+           !priv->p2p.is_wilc_ie) {
+               for (i = P2P_PUB_ACTION_SUBTYPE; i < size; i++) {
+                       if (!memcmp(p2p_vendor_spec, &buff[i], 6)) {
+                               priv->p2p.recv_random = buff[i + 6];
+                               priv->p2p.is_wilc_ie = true;
+                               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                          "WILC Vendor specific IE:%02x\n",
+                                          priv->p2p.recv_random);
+                               break;
+                       }
+               }
+       }
+
+       if (priv->p2p.local_random <= priv->p2p.recv_random) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n",
+                          priv->p2p.local_random, priv->p2p.recv_random);
+               return;
+       }
+
+       if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP ||
+           subtype == P2P_INV_REQ || subtype == P2P_INV_RSP) {
+               for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < size; i++) {
+                       if (buff[i] == P2PELEM_ATTR_ID &&
+                           !(memcmp(p2p_oui, &buff[i + 2], 4))) {
+                               bool p2p_mode = vif->attr_sysfs.p2p_mode;
+
+                               wilc_wfi_cfg_parse_rx_action(vif, &buff[i + 6],
+                                                            size - (i + 6),
+                                                            vif->wilc->sta_ch,
+                                                            p2p_mode);
+                               break;
+                       }
+               }
+       }
+}
+
+void wilc_wfi_p2p_rx(struct net_device *dev, u8 *buff, u32 size)
+{
+       struct wilc_priv *priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+       struct host_if_drv *wfi_drv = priv->hif_drv;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wl = vif->wilc;
+       u32 header, pkt_offset;
+       s32 freq;
+       __le16 fc;
+
+       memcpy(&header, (buff - HOST_HDR_OFFSET), HOST_HDR_OFFSET);
+
+       pkt_offset = GET_PKT_OFFSET(header);
+
+       fc = ((struct ieee80211_hdr *)buff)->frame_control;
+       if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
+               bool ack = false;
+
+               if (ieee80211_is_probe_resp(fc) ||
+                   pkt_offset & IS_MGMT_STATUS_SUCCES)
+                       ack = true;
+
+               cfg80211_mgmt_tx_status(priv->wdev, priv->tx_cookie, buff, size,
+                                       ack, GFP_KERNEL);
+               return;
+       }
+
+       PRINT_D(vif->ndev, GENERIC_DBG, "Rx Frame Type:%x\n", fc);
+
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+       freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ);
+ #else
+       freq = ieee80211_channel_to_frequency(wl->op_ch, IEEE80211_BAND_2GHZ);
+ #endif
+       if (!ieee80211_is_action(fc)) {
+               cfg80211_rx_mgmt(priv->wdev, freq, 0, buff, size, 0);
+               return;
+       }
+
+       PRINT_D(vif->ndev, GENERIC_DBG,
+                  "Rx Action Frame Type: %x %x\n",
+                  buff[ACTION_SUBTYPE_ID],
+                  buff[P2P_PUB_ACTION_SUBTYPE]);
+       if (priv->cfg_scanning &&
+           time_after_eq(jiffies, (unsigned long)wfi_drv->p2p_timeout)) {
+               PRINT_WRN(dev, GENERIC_DBG, "Receiving action wrong ch\n");
+               return;
+       }
+       if (buff[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) {
+               u8 subtype = buff[P2P_PUB_ACTION_SUBTYPE];
+
+               switch (buff[ACTION_SUBTYPE_ID]) {
+               case GAS_INITIAL_REQ:
+                       PRINT_D(vif->ndev, GENERIC_DBG,
+                                  "GAS INITIAL REQ %x\n",
+                                  buff[ACTION_SUBTYPE_ID]);
+                       break;
+
+               case GAS_INITIAL_RSP:
+                       PRINT_D(vif->ndev, GENERIC_DBG,
+                                  "GAS INITIAL RSP %x\n",
+                                  buff[ACTION_SUBTYPE_ID]);
+                       break;
+
+               case PUBLIC_ACT_VENDORSPEC:
+                       if (!memcmp(p2p_oui, &buff[ACTION_SUBTYPE_ID + 1], 4))
+                               wilc_wfi_cfg_parse_rx_vendor_spec(priv, buff,
+                                                                 size);
+
+                       if ((subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) &&
+                           priv->p2p.is_wilc_ie)
+                               size -= 7;
+
+                       break;
+
+               default:
+                       PRINT_WRN(dev, GENERIC_DBG,
+                                  "Not handled action frame type:%x\n",
+                                  buff[ACTION_SUBTYPE_ID]);
+                       break;
+               }
+       }
+
+       cfg80211_rx_mgmt(priv->wdev, freq, 0, buff, size, 0);
+}
+
+static void wilc_wfi_mgmt_tx_complete(void *priv, int status)
+{
+       struct wilc_p2p_mgmt_data *pv_data = priv;
+
+       kfree(pv_data->buff);
+       kfree(pv_data);
+}
+
+static void wilc_wfi_remain_on_channel_expired(void *data, u64 cookie)
+{
+       struct wilc_priv *priv = data;
+       struct wilc_wfi_p2p_listen_params *params = &priv->remain_on_ch_params;
+
+       if (cookie != priv->remain_on_ch_params.listen_cookie) {
+               PRINT_INFO(priv->dev, GENERIC_DBG,
+                          "Received cookies didn't match received[%llu] Expected[%llu]\n",
+                          cookie, priv->remain_on_ch_params.listen_cookie);
+               return;
+       }
+
+       priv->p2p_listen_state = false;
+
+       cfg80211_remain_on_channel_expired(priv->wdev, cookie,
+                                          params->listen_ch, GFP_KERNEL);
+}
+
+static int remain_on_channel(struct wiphy *wiphy,
+                            struct wireless_dev *wdev,
+                            struct ieee80211_channel *chan,
+                            unsigned int duration, u64 *cookie)
+{
+       int ret = 0;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       u64 id;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "Remaining on channel [%d]\n",
+                  chan->hw_value);
+
+       if (wdev->iftype == NL80211_IFTYPE_AP) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Required while in AP mode\n");
+               return ret;
+       }
+
+       id = ++priv->inc_roc_cookie;
+       if (id == 0)
+               id = ++priv->inc_roc_cookie;
+
+       ret = wilc_remain_on_channel(vif, id, duration, chan->hw_value,
+                                    wilc_wfi_remain_on_channel_expired,
+                                    (void *)priv);
+       if (ret)
+               return ret;
+
+       vif->wilc->op_ch = chan->hw_value;
+       priv->remain_on_ch_params.listen_ch = chan;
+       priv->remain_on_ch_params.listen_cookie = id;
+       *cookie = id;
+       priv->remain_on_ch_params.listen_duration = duration;
+       priv->p2p_listen_state = true;
+
+       cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
+
+#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
+       vif->hif_drv->remain_on_ch_timer.data = (unsigned long)vif->hif_drv;
+#endif
+       mod_timer(&vif->hif_drv->remain_on_ch_timer,
+                 jiffies + msecs_to_jiffies(duration));
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                  "Remaining on duration [%d] [%llu]\n",
+                  duration, priv->remain_on_ch_params.listen_cookie);
+
+       return ret;
+}
+
+static int cancel_remain_on_channel(struct wiphy *wiphy,
+                                   struct wireless_dev *wdev,
+                                   u64 cookie)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "cookie received[%llu] expected[%llu]\n",
+                  cookie, priv->remain_on_ch_params.listen_cookie);
+       if (cookie != priv->remain_on_ch_params.listen_cookie)
+               return -ENOENT;
+
+       return wilc_listen_state_expired(vif, cookie);
+}
+
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv,
+                                       struct wilc_p2p_mgmt_data *mgmt_tx,
+                                       struct cfg80211_mgmt_tx_params *params,
+                                       u8 iftype, u32 buf_len)
+#else
+static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv,
+                                       struct wilc_p2p_mgmt_data *mgmt_tx,
+                                       const u8 *buf, size_t len,
+                                       u8 iftype, u32 buf_len)
+#endif
+{
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+       const u8 *buf = params->buf;
+       size_t len = params->len;
+#endif
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       u32 i;
+       u8 subtype = buf[P2P_PUB_ACTION_SUBTYPE];
+
+       if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) {
+               if (priv->p2p.local_random == 1 &&
+                   priv->p2p.recv_random < priv->p2p.local_random) {
+                       get_random_bytes(&priv->p2p.local_random, 1);
+                       priv->p2p.local_random++;
+               }
+       }
+
+       if (priv->p2p.local_random <= priv->p2p.recv_random ||
+           !(subtype == GO_NEG_REQ || subtype == GO_NEG_RSP ||
+             subtype == P2P_INV_REQ || subtype == P2P_INV_RSP))
+               return;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                  "LOCAL WILL BE GO LocaRand=%02x RecvRand %02x\n",
+                  priv->p2p.local_random, priv->p2p.recv_random);
+       for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < len; i++) {
+               if (buf[i] == P2PELEM_ATTR_ID &&
+                   !memcmp(p2p_oui, &buf[i + 2], 4)) {
+                       bool oper_ch = false;
+                       u8 *tx_buff = &mgmt_tx->buff[i + 6];
+
+                       if (subtype == P2P_INV_REQ || subtype == P2P_INV_RSP)
+                               oper_ch = true;
+
+                       wilc_wfi_cfg_parse_tx_action(vif, tx_buff,
+                                                    len - (i + 6), oper_ch,
+                                                    vif->wilc->sta_ch,
+                                                    vif->attr_sysfs.p2p_mode);
+                       break;
+               }
+       }
+
+       if (subtype != P2P_INV_REQ && subtype != P2P_INV_RSP) {
+               int vendor_spec_len = sizeof(p2p_vendor_spec);
+
+               memcpy(&mgmt_tx->buff[len], p2p_vendor_spec,
+                      vendor_spec_len);
+               mgmt_tx->buff[len + vendor_spec_len] = priv->p2p.local_random;
+               mgmt_tx->size = buf_len;
+       }
+}
+
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+static int mgmt_tx(struct wiphy *wiphy,
+                  struct wireless_dev *wdev,
+                  struct cfg80211_mgmt_tx_params *params,
+                  u64 *cookie)
+#else
+static int mgmt_tx(struct wiphy *wiphy,
+                  struct wireless_dev *wdev,
+                  struct ieee80211_channel *chan, bool offchan,
+                  unsigned int wait, const u8 *buf, size_t len,
+                  bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+#endif
+{
+#if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+       struct ieee80211_channel *chan = params->chan;
+       unsigned int wait = params->wait;
+       const u8 *buf = params->buf;
+       size_t len = params->len;
+#endif
+       const struct ieee80211_mgmt *mgmt;
+       struct wilc_p2p_mgmt_data *mgmt_tx;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct host_if_drv *wfi_drv = priv->hif_drv;
+       struct wilc_vif *vif = netdev_priv(wdev->netdev);
+       u32 buf_len = len + sizeof(p2p_vendor_spec) +
+                     sizeof(priv->p2p.local_random);
+       int ret = 0;
+
+       *cookie = prandom_u32();
+       priv->tx_cookie = *cookie;
+       mgmt = (const struct ieee80211_mgmt *)buf;
+
+       if (!ieee80211_is_mgmt(mgmt->frame_control)) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "This function transmits only management frames\n");
+               goto out;
+       }
+
+       mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_KERNEL);
+       if (!mgmt_tx) {
+               PRINT_ER(vif->ndev,
+                        "Failed to allocate memory for mgmt_tx structure\n");
+               return -ENOMEM;
+       }
+
+       mgmt_tx->buff = kmalloc(buf_len, GFP_KERNEL);
+       if (!mgmt_tx->buff) {
+               ret = -ENOMEM;
+               PRINT_ER(vif->ndev,
+                        "Failed to allocate memory for mgmt_tx buff\n");
+               kfree(mgmt_tx);
+               goto out;
+       }
+
+       memcpy(mgmt_tx->buff, buf, len);
+       mgmt_tx->size = len;
+
+       if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG, "TX: Probe Response\n");
+               PRINT_INFO(vif->ndev, GENERIC_DBG, "Setting channel: %d\n",
+                          chan->hw_value);
+               wilc_set_mac_chnl_num(vif, chan->hw_value);
+               vif->wilc->op_ch = chan->hw_value;
+               goto out_txq_add_pkt;
+       }
+
+       if (!ieee80211_is_action(mgmt->frame_control))
+               goto out_txq_add_pkt;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "ACTION FRAME:%x\n",
+                  (u16)mgmt->frame_control);
+       if (buf[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) {
+               if (buf[ACTION_SUBTYPE_ID] != PUBLIC_ACT_VENDORSPEC ||
+                   buf[P2P_PUB_ACTION_SUBTYPE] != GO_NEG_CONF) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Setting channel: %d\n",
+                                  chan->hw_value);
+                       wilc_set_mac_chnl_num(vif,
+                                             chan->hw_value);
+                       vif->wilc->op_ch = chan->hw_value;
+               }
+               switch (buf[ACTION_SUBTYPE_ID]) {
+               case GAS_INITIAL_REQ:
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "GAS INITIAL REQ %x\n",
+                                  buf[ACTION_SUBTYPE_ID]);
+                       break;
+
+               case GAS_INITIAL_RSP:
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "GAS INITIAL RSP %x\n",
+                                  buf[ACTION_SUBTYPE_ID]);
+                       break;
+
+               case PUBLIC_ACT_VENDORSPEC:
+                       if (!memcmp(p2p_oui, &buf[ACTION_SUBTYPE_ID + 1], 4))
+                       #if KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
+                               wilc_wfi_cfg_tx_vendor_spec(priv, mgmt_tx,
+                                                           params,
+                                                           vif->iftype,
+                                                           buf_len);
+                       #else
+                               wilc_wfi_cfg_tx_vendor_spec(priv, mgmt_tx, buf,
+                                                           len, vif->iftype,
+                                                           buf_len);
+                       #endif
+                       else
+                               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                          "Not a P2P public action frame\n");
+
+                       break;
+
+               default:
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Not handled action frame type:%x\n",
+                                  buf[ACTION_SUBTYPE_ID]);
+                       break;
+               }
+       }
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                  "TX: ACTION FRAME Type:%x : Chan:%d\n",
+                  buf[ACTION_SUBTYPE_ID], chan->hw_value);
+       wfi_drv->p2p_timeout = (jiffies + msecs_to_jiffies(wait));
+
+out_txq_add_pkt:
+
+       txq_add_mgmt_pkt(wdev->netdev, mgmt_tx,  mgmt_tx->buff, mgmt_tx->size,
+                        wilc_wfi_mgmt_tx_complete);
+
+out:
+
+       return ret;
+}
+
+static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
+                              struct wireless_dev *wdev,
+                              u64 cookie)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct host_if_drv *wfi_drv = priv->hif_drv;
+
+       wfi_drv->p2p_timeout = jiffies;
+
+       if (!priv->p2p_listen_state) {
+               struct wilc_wfi_p2p_listen_params *params;
+
+               params = &priv->remain_on_ch_params;
+
+               cfg80211_remain_on_channel_expired(priv->wdev,
+                                                  params->listen_cookie,
+                                                  params->listen_ch,
+                                                  GFP_KERNEL);
+       }
+
+       return 0;
+}
+
+void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
+                             u16 frame_type, bool reg)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->wdev->netdev);
+       struct wilc *wl = vif->wilc;
+
+       if (!frame_type)
+               return;
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                  "Frame registering Frame Type: %x: Boolean: %d\n",
+                  frame_type, reg);
+       switch (frame_type) {
+       case IEEE80211_STYPE_PROBE_REQ:
+               vif->frame_reg[0].type = frame_type;
+               vif->frame_reg[0].reg = reg;
+               break;
+
+       case IEEE80211_STYPE_ACTION:
+               vif->frame_reg[1].type = frame_type;
+               vif->frame_reg[1].reg = reg;
+               break;
+
+       default:
+               break;
+       }
+
+       if (!wl->initialized) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Return since mac is closed\n");
+               return;
+       }
+       wilc_frame_register(vif, frame_type, reg);
+}
+
+static int set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev,
+                              s32 rssi_thold, u32 rssi_hyst)
+{
+       PRINT_INFO(dev, CFG80211_DBG, "Setting CQM RSSi Function\n");
+       return 0;
+}
+
+static int dump_station(struct wiphy *wiphy, struct net_device *dev,
+                       int idx, u8 *mac, struct station_info *sinfo)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       int ret;
+
+       if (idx != 0)
+               return -ENOENT;
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Dumping station information\n");
+
+       ret = wilc_get_rssi(vif, &sinfo->signal);
+       if (ret)
+               return ret;
+
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+       sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+#else
+       sinfo->filled |= STATION_INFO_SIGNAL;
+#endif
+
+       return 0;
+}
+
+static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                         bool enabled, int timeout)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       if (!priv->hif_drv) {
+               PRINT_ER(dev, "hif driver is NULL\n");
+               return -EIO;
+       }
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       /* Can't set PS during obtaining IP */
+       if (vif->obtaining_ip == true) {
+               PRINT_ER(dev,
+                        "Device obtaining IP, Power Managment will be handled after IP Obtained\n");
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Save the Current state of the PS = %d\n", enabled);
+
+               /* Save the current status of the PS */
+               store_power_save_current_state(vif, enabled);
+
+               return 0;
+       }
+#endif
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  " Power save Enabled= %d , TimeOut = %d\n", enabled,
+                  timeout);
+
+       wilc_set_power_mgmt(vif, enabled, timeout);
+
+       return 0;
+}
+
+#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE
+static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
+                              enum nl80211_iftype type,
+                              struct vif_params *params)
+#else
+static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
+                              enum nl80211_iftype type, u32 *flags,
+                              struct vif_params *params)
+#endif
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wl = vif->wilc;
+       struct net_device *net_device_1 = wilc_get_if_netdev(wl, WILC_P2P_IFC);
+       struct net_device *net_device_2 = wilc_get_if_netdev(wl, WILC_WLAN_IFC);
+       struct wilc_vif *vif_1 = netdev_priv(net_device_1);
+       struct wilc_vif *vif_2 = netdev_priv(net_device_2);
+
+       PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                  "In Change virtual interface function\n");
+       PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                  "Wireless interface name =%s\n", dev->name);
+       priv->p2p.local_random = 0x01;
+       priv->p2p.recv_random = 0x00;
+       priv->p2p.is_wilc_ie = false;
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                  "Changing virtual interface, enable scan\n");
+       handle_pwrsave_for_IP(vif, IP_STATE_DEFAULT);
+#endif
+
+       switch (type) {
+       case NL80211_IFTYPE_STATION:
+               vif->connecting = false;
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Interface type = NL80211_IFTYPE_STATION\n");
+               dev->ieee80211_ptr->iftype = type;
+               priv->wdev->iftype = type;
+               vif->monitor_flag = 0;
+               vif->iftype = WILC_STATION_MODE;
+               wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+                                        WILC_STATION_MODE, vif->ifc_id);
+               wilc_set_operation_mode(vif, WILC_STATION_MODE);
+
+               memset(priv->assoc_stainfo.sta_associated_bss, 0,
+                      WILC_MAX_NUM_STA * ETH_ALEN);
+
+               wilc_set_power_mgmt(vif_1, 1, 0);
+               wilc_set_power_mgmt(vif_2, 1, 0);
+               break;
+
+       case NL80211_IFTYPE_P2P_CLIENT:
+               vif->connecting = false;
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Interface type = NL80211_IFTYPE_P2P_CLIENT\n");
+               dev->ieee80211_ptr->iftype = type;
+               priv->wdev->iftype = type;
+               vif->monitor_flag = 0;
+               vif->iftype = WILC_CLIENT_MODE;
+               wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+                                        WILC_STATION_MODE, vif->ifc_id);
+               wilc_set_operation_mode(vif, WILC_STATION_MODE);
+
+               wilc_set_power_mgmt(vif_1, 0, 0);
+               wilc_set_power_mgmt(vif_2, 0, 0);
+               break;
+
+       case NL80211_IFTYPE_AP:
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Interface type = NL80211_IFTYPE_AP\n");
+               dev->ieee80211_ptr->iftype = type;
+               priv->wdev->iftype = type;
+               vif->iftype = WILC_AP_MODE;
+               if (wl->initialized) {
+                       wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+                                                WILC_AP_MODE, vif->ifc_id);
+                       wilc_set_operation_mode(vif, WILC_AP_MODE);
+                       wilc_set_power_mgmt(vif_1, 0, 0);
+                       wilc_set_power_mgmt(vif_2, 0, 0);
+               }
+               break;
+
+       case NL80211_IFTYPE_P2P_GO:
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Interface type = NL80211_IFTYPE_GO\n");
+               PRINT_INFO(vif->ndev, GENERIC_DBG, "start duringIP timer\n");
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+               handle_pwrsave_for_IP(vif, IP_STATE_GO_ASSIGNING);
+#endif
+               dev->ieee80211_ptr->iftype = type;
+               priv->wdev->iftype = type;
+               vif->iftype = WILC_GO_MODE;
+               wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+                                       WILC_AP_MODE, vif->ifc_id);
+               wilc_set_operation_mode(vif, WILC_AP_MODE);
+               wilc_set_power_mgmt(vif_1, 0, 0);
+               wilc_set_power_mgmt(vif_2, 0, 0);
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Interface type = NL80211_IFTYPE_MONITOR\n");
+               dev->ieee80211_ptr->iftype = type;
+               dev->type = ARPHRD_IEEE80211_RADIOTAP;
+               priv->wdev->iftype = type;
+               vif->iftype = WILC_MONITOR_MODE;
+               if (wl->initialized) {
+                       wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+                                                WILC_MONITOR_MODE,
+                                                vif->ifc_id);
+
+                       wilc_set_operation_mode(vif, WILC_MONITOR_MODE);
+                       wilc_set_power_mgmt(vif_1, 0, 0);
+                       wilc_set_power_mgmt(vif_2, 0, 0);
+               }
+               break;
+
+       default:
+               PRINT_ER(dev, "Unknown interface type= %d\n", type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int start_ap(struct wiphy *wiphy, struct net_device *dev,
+                   struct cfg80211_ap_settings *settings)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       int ret;
+
+       PRINT_INFO(vif->ndev, HOSTAPD_DBG, "Starting ap\n");
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                  "Interval= %d\n DTIM period= %d\n Head length= %d Tail length= %d\n",
+                  settings->beacon_interval, settings->dtim_period,
+                  settings->beacon.head_len, settings->beacon.tail_len);
+       ret = set_channel(wiphy, &settings->chandef);
+
+       if (ret != 0)
+               PRINT_ER(dev, "Error in setting channel\n");
+
+       wilc_wlan_set_bssid(dev, dev->dev_addr, WILC_AP_MODE);
+       wilc_set_power_mgmt(vif, 0, 0);
+
+       return wilc_add_beacon(vif, settings->beacon_interval,
+                              settings->dtim_period, &settings->beacon);
+}
+
+static int change_beacon(struct wiphy *wiphy, struct net_device *dev,
+                        struct cfg80211_beacon_data *beacon)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, HOSTAPD_DBG, "Setting beacon\n");
+
+       return wilc_add_beacon(vif, 0, 0, beacon);
+}
+
+static int stop_ap(struct wiphy *wiphy, struct net_device *dev)
+{
+       int ret;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Deleting beacon\n");
+
+       wilc_wlan_set_bssid(dev, NULL, WILC_AP_MODE);
+
+       ret = wilc_del_beacon(vif);
+
+       if (ret)
+               PRINT_ER(dev, "Host delete beacon fail\n");
+
+       return ret;
+}
+
+#if KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int add_station(struct wiphy *wiphy, struct net_device *dev,
+                      const u8 *mac, struct station_parameters *params)
+#else
+static int add_station(struct wiphy *wiphy, struct net_device *dev,
+                      u8 *mac, struct station_parameters *params)
+#endif
+{
+       int ret = 0;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(dev);
+       u8 *assoc_bss = priv->assoc_stainfo.sta_associated_bss[params->aid];
+
+       if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+               memcpy(assoc_bss, mac, ETH_ALEN);
+
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Adding station parameters %d\n", params->aid);
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n",
+                          assoc_bss[0], assoc_bss[1], assoc_bss[2],
+                          assoc_bss[3], assoc_bss[4], assoc_bss[5]);
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG, "ASSOC ID = %d\n",
+                          params->aid);
+               PRINT_INFO(vif->ndev, HOSTAPD_DBG,
+                          "Number of supported rates = %d\n",
+                          params->supported_rates_len);
+
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "IS HT supported = %d\n",
+                          (!params->ht_capa) ? false : true);
+
+               if (params->ht_capa) {
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Capability Info = %d\n",
+                                  params->ht_capa->cap_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "AMPDU Params = %d\n",
+                                  params->ht_capa->ampdu_params_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "HT Extended params= %d\n",
+                                  params->ht_capa->extended_ht_cap_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Tx Beamforming Cap= %d\n",
+                                  params->ht_capa->tx_BF_cap_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Antenna selection info = %d\n",
+                                  params->ht_capa->antenna_selection_info);
+               }
+
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Mask = %d\n",
+                          params->sta_flags_mask);
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Set = %d\n",
+                          params->sta_flags_set);
+               ret = wilc_add_station(vif, (const u8 *)mac, params);
+               if (ret)
+                       PRINT_ER(dev, "Host add station fail\n");
+       }
+
+       return ret;
+}
+
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+static int del_station(struct wiphy *wiphy, struct net_device *dev,
+                      struct station_del_parameters *params)
+#elif KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int del_station(struct wiphy *wiphy, struct net_device *dev,
+                      const u8 *mac)
+#else
+static int del_station(struct wiphy *wiphy, struct net_device *dev,
+                      u8 *mac)
+#endif
+{
+#if KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE
+       const u8 *mac = params->mac;
+#endif
+       int ret = 0;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct sta_info *info;
+
+       if (!(vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE))
+               return ret;
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Deleting station\n");
+
+       info = &priv->assoc_stainfo;
+
+       if (!mac) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "All associated stations\n");
+               ret = wilc_del_allstation(vif, info->sta_associated_bss);
+       } else {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "With mac address: %x%x%x%x%x%x\n",
+                          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+       }
+
+       ret = wilc_del_station(vif, mac);
+       if (ret)
+               PRINT_ER(dev, "Host delete station fail\n");
+
+       return ret;
+}
+
+#if KERNEL_VERSION(3, 16, 0) <= LINUX_VERSION_CODE
+static int change_station(struct wiphy *wiphy, struct net_device *dev,
+                         const u8 *mac, struct station_parameters *params)
+#else
+static int change_station(struct wiphy *wiphy, struct net_device *dev,
+                         u8 *mac, struct station_parameters *params)
+#endif
+{
+       int ret = 0;
+       struct wilc_vif *vif = netdev_priv(dev);
+
+       PRINT_D(vif->ndev, CFG80211_DBG, "Change station paramters\n");
+
+       if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n",
+                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "ASSOC ID = %d\n",
+                          params->aid);
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Number of supported rates = %d\n",
+                          params->supported_rates_len);
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "IS HT supported = %d\n",
+                          (!params->ht_capa) ? false : true);
+               if (params->ht_capa) {
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Capability Info = %d\n",
+                                  params->ht_capa->cap_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "AMPDU Params = %d\n",
+                                  params->ht_capa->ampdu_params_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "HT Extended params= %d\n",
+                                  params->ht_capa->extended_ht_cap_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Tx Beamforming Cap= %d\n",
+                                  params->ht_capa->tx_BF_cap_info);
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                                  "Antenna selection info = %d\n",
+                                  params->ht_capa->antenna_selection_info);
+               }
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Mask = %d\n",
+                          params->sta_flags_mask);
+               PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Set = %d\n",
+                          params->sta_flags_set);
+               ret = wilc_edit_station(vif, (const u8 *)mac, params);
+               if (ret)
+                       PRINT_ER(dev, "Host edit station fail\n");
+       }
+       return ret;
+}
+
+#if KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+                                            const char *name,
+                                            unsigned char name_assign_type,
+                                            enum nl80211_iftype type,
+                                            struct vif_params *params)
+#elif KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+                                            const char *name,
+                                            unsigned char name_assign_type,
+                                            enum nl80211_iftype type,
+                                            u32 *flags,
+                                            struct vif_params *params)
+#elif KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+                                            const char *name,
+                                            enum nl80211_iftype type,
+                                            u32 *flags,
+                                            struct vif_params *params)
+#else
+static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
+                                            char *name,
+                                            enum nl80211_iftype type,
+                                            u32 *flags,
+                                            struct vif_params *params)
+#endif
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->wdev->netdev);
+       struct net_device *new_ifc;
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Adding monitor interface[%p]\n",
+                  priv->wdev->netdev);
+
+       if (type == NL80211_IFTYPE_MONITOR) {
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Initializing mon ifc virtual device driver\n");
+               PRINT_INFO(vif->ndev, CFG80211_DBG,
+                          "Adding monitor interface[%p]\n", vif->ndev);
+               new_ifc = wilc_wfi_init_mon_interface(vif->wilc, name,
+                                                     vif->ndev);
+               if (new_ifc) {
+                       PRINT_INFO(vif->ndev, CFG80211_DBG,
+                       "Setting monitor flag in private structure\n");
+                       vif = netdev_priv(priv->wdev->netdev);
+                       vif->monitor_flag = 1;
+               } else {
+                       PRINT_ER(vif->ndev,
+                                "Error in initializing monitor interface\n");
+               }
+       }
+       return priv->wdev;
+}
+
+static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+
+       PRINT_INFO(priv->dev, HOSTAPD_DBG, "Deleting virtual interface\n");
+       return 0;
+}
+
+static int wilc_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+
+       if (!wow)
+               PRINT_INFO(priv->dev, GENERIC_DBG,
+                          "No wake up triggers defined\n");
+       else if (wow->any == 0)
+               PRINT_INFO(priv->dev, GENERIC_DBG,
+                          "The only supported wake up trigger (any) is not set\n");
+
+       return 0;
+}
+
+static int wilc_resume(struct wiphy *wiphy)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "cfg resume\n");
+       return 0;
+}
+
+static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled)
+{
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, GENERIC_DBG, "cfg set wake up = %d\n", enabled);
+       wilc_set_wowlan_trigger(vif, (u8)enabled);
+}
+
+static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                       enum nl80211_tx_power_setting type, int mbm)
+{
+       int ret;
+       s32 tx_power = MBM_TO_DBM(mbm);
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting tx power %d\n", tx_power);
+       if (tx_power < 0)
+               tx_power = 0;
+       else if (tx_power > 18)
+               tx_power = 18;
+       ret = wilc_set_tx_power(vif, tx_power);
+       if (ret)
+               PRINT_ER(vif->ndev, "Failed to set tx power\n");
+
+       return ret;
+}
+
+static int get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                       int *dbm)
+{
+       int ret;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+       struct wilc *wl = vif->wilc;
+
+       /* If firmware is not started, return. */
+       if (!wl->initialized)
+               return -EIO;
+       *dbm = 0;
+       ret = wilc_get_tx_power(vif, (u8 *)dbm);
+       if (ret)
+               PRINT_ER(vif->ndev, "Failed to get tx power\n");
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Got tx power %d\n", *dbm);
+
+       return ret;
+}
+
+static int set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
+{
+       int ret;
+       struct wilc_priv *priv = wiphy_priv(wiphy);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(vif->ndev, CFG80211_DBG, "Select antenna mode %d\n", tx_ant);
+       if (!tx_ant || !rx_ant)
+               return -EINVAL;
+
+       ret = wilc_set_antenna(vif, (u8)(tx_ant-1));
+       if (ret)
+               PRINT_ER(vif->ndev, "Failed to set tx antenna\n");
+
+       return ret;
+}
+
+static const struct cfg80211_ops wilc_cfg80211_ops = {
+       .set_monitor_channel = set_channel,
+       .scan = scan,
+       .connect = connect,
+       .disconnect = disconnect,
+       .add_key = add_key,
+       .del_key = del_key,
+       .get_key = get_key,
+       .set_default_key = set_default_key,
+       .add_virtual_intf = add_virtual_intf,
+       .del_virtual_intf = del_virtual_intf,
+       .change_virtual_intf = change_virtual_intf,
+
+       .start_ap = start_ap,
+       .change_beacon = change_beacon,
+       .stop_ap = stop_ap,
+       .add_station = add_station,
+       .del_station = del_station,
+       .change_station = change_station,
+       .get_station = get_station,
+       .dump_station = dump_station,
+       .change_bss = change_bss,
+       .set_wiphy_params = set_wiphy_params,
+
+       .set_pmksa = set_pmksa,
+       .del_pmksa = del_pmksa,
+       .flush_pmksa = flush_pmksa,
+       .remain_on_channel = remain_on_channel,
+       .cancel_remain_on_channel = cancel_remain_on_channel,
+       .mgmt_tx_cancel_wait = mgmt_tx_cancel_wait,
+       .mgmt_tx = mgmt_tx,
+       .mgmt_frame_register = wilc_mgmt_frame_register,
+       .set_power_mgmt = set_power_mgmt,
+       .set_cqm_rssi_config = set_cqm_rssi_config,
+
+       .suspend = wilc_suspend,
+       .resume = wilc_resume,
+       .set_wakeup = wilc_set_wakeup,
+       .set_tx_power = set_tx_power,
+       .get_tx_power = get_tx_power,
+       .set_antenna = set_antenna,
+};
+
+static struct wireless_dev *wilc_wfi_cfg_alloc(struct net_device *net)
+{
+       struct wireless_dev *wdev;
+
+       PRINT_INFO(net, CFG80211_DBG, "Allocating wireless device\n");
+       wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
+       if (!wdev) {
+               PRINT_ER(net, "Cannot allocate wireless device\n");
+               goto out;
+       }
+
+       wdev->wiphy = wiphy_new(&wilc_cfg80211_ops, sizeof(struct wilc_priv));
+       if (!wdev->wiphy) {
+               PRINT_ER(net, "Cannot allocate wiphy\n");
+               goto free_mem;
+       }
+
+       return wdev;
+
+free_mem:
+       kfree(wdev);
+out:
+       return NULL;
+}
+
+struct wireless_dev *wilc_create_wiphy(struct net_device *net,
+                                      struct device *dev)
+{
+       struct wilc_priv *priv;
+       struct wireless_dev *wdev;
+       int ret;
+
+       PRINT_INFO(net, CFG80211_DBG, "Registering wifi device\n");
+       wdev = wilc_wfi_cfg_alloc(net);
+       if (!wdev) {
+               PRINT_ER(net, "wiphy new allocate failed\n");
+               return NULL;
+       }
+
+       priv = wdev_priv(wdev);
+       priv->wdev = wdev;
+
+       memcpy(priv->bitrates, wilc_bitrates, sizeof(wilc_bitrates));
+       memcpy(priv->channels, wilc_2ghz_channels, sizeof(wilc_2ghz_channels));
+       priv->band.bitrates = priv->bitrates;
+       priv->band.n_bitrates = ARRAY_SIZE(priv->bitrates);
+       priv->band.channels = priv->channels;
+       priv->band.n_channels = ARRAY_SIZE(wilc_2ghz_channels);
+
+       priv->band.ht_cap.ht_supported = 1;
+       priv->band.ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+       priv->band.ht_cap.mcs.rx_mask[0] = 0xff;
+       priv->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+       priv->band.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+
+#if KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
+       wdev->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
+#else
+       wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+#endif
+
+       wdev->wiphy->max_scan_ssids = WILC_MAX_NUM_PROBED_SSID;
+#if KERNEL_VERSION(3, 11, 0) <= LINUX_VERSION_CODE
+       wdev->wiphy->wowlan = &wowlan_support;
+#else
+       wdev->wiphy->wowlan = wowlan_support;
+#endif
+       wdev->wiphy->max_num_pmkids = WILC_MAX_NUM_PMKIDS;
+       PRINT_D(net, CFG80211_DBG, "Max number of PMKIDs = %d\n",
+               wdev->wiphy->max_num_pmkids);
+       wdev->wiphy->max_scan_ie_len = 1000;
+       wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+       memcpy(priv->cipher_suites, wilc_cipher_suites,
+              sizeof(wilc_cipher_suites));
+       wdev->wiphy->cipher_suites = priv->cipher_suites;
+       wdev->wiphy->n_cipher_suites = ARRAY_SIZE(wilc_cipher_suites);
+       wdev->wiphy->available_antennas_tx = 0x3;
+       wdev->wiphy->available_antennas_rx = 0x3;
+       wdev->wiphy->mgmt_stypes = wilc_wfi_cfg80211_mgmt_types;
+
+       wdev->wiphy->max_remain_on_channel_duration = 500;
+       wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                       BIT(NL80211_IFTYPE_AP) |
+                                       BIT(NL80211_IFTYPE_MONITOR) |
+                                       BIT(NL80211_IFTYPE_P2P_GO) |
+                                       BIT(NL80211_IFTYPE_P2P_CLIENT);
+       wdev->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       wdev->iftype = NL80211_IFTYPE_STATION;
+
+       PRINT_D(net, CFG80211_DBG,
+               "Max scan ids= %d,Max scan IE len= %d,Signal Type= %d,Interface Modes= %d,Interface Type= %d\n",
+               wdev->wiphy->max_scan_ssids, wdev->wiphy->max_scan_ie_len,
+               wdev->wiphy->signal_type, wdev->wiphy->interface_modes,
+               wdev->iftype);
+
+       set_wiphy_dev(wdev->wiphy, dev);
+
+       ret = wiphy_register(wdev->wiphy);
+       if (ret) {
+               PRINT_ER(net, "Cannot register wiphy device\n");
+               wiphy_free(wdev->wiphy);
+               kfree(wdev);
+               return NULL;
+       }
+
+       priv->dev = net;
+       return wdev;
+}
+
+int wilc_init_host_int(struct net_device *net)
+{
+       int ret;
+       struct wilc_priv *priv = wdev_priv(net->ieee80211_ptr);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       PRINT_INFO(net, INIT_DBG, "Host[%p][%p]\n", net, net->ieee80211_ptr);
+
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+       #ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       timer_setup(&vif->during_ip_timer, clear_during_ip, 0);
+       #endif
+       timer_setup(&priv->eap_buff_timer, eap_buff_timeout, 0);
+#else
+       #ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       setup_timer(&vif->during_ip_timer, clear_during_ip, 0);
+       #endif
+       setup_timer(&priv->eap_buff_timer, eap_buff_timeout, 0);
+#endif
+
+       priv->p2p_listen_state = false;
+
+       mutex_init(&priv->scan_req_lock);
+       ret = wilc_init(net, &priv->hif_drv);
+       if (ret)
+               PRINT_ER(net, "Error while initializing hostinterface\n");
+
+       return ret;
+}
+
+void wilc_deinit_host_int(struct net_device *net)
+{
+       int ret;
+       struct wilc_priv *priv = wdev_priv(net->ieee80211_ptr);
+       struct wilc_vif *vif = netdev_priv(priv->dev);
+
+       priv->p2p_listen_state = false;
+
+       mutex_destroy(&priv->scan_req_lock);
+       ret = wilc_deinit(vif);
+
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       del_timer_sync(&vif->during_ip_timer);
+#endif
+       del_timer_sync(&priv->eap_buff_timer);
+
+       if (ret)
+               PRINT_ER(net, "Error while deinitializing host interface\n");
+}
+
+void wilc_free_wiphy(struct net_device *net)
+{
+       PRINT_INFO(net, CFG80211_DBG, "Unregistering wiphy\n");
+       if (!net) {
+               PRINT_INFO(net, INIT_DBG, "net_device is NULL\n");
+               return;
+       }
+
+       if (!net->ieee80211_ptr) {
+               PRINT_INFO(net, INIT_DBG, "ieee80211_ptr is NULL\n");
+               return;
+       }
+
+       if (!net->ieee80211_ptr->wiphy) {
+               PRINT_INFO(net, INIT_DBG, "wiphy is NULL\n");
+               return;
+       }
+
+       wiphy_unregister(net->ieee80211_ptr->wiphy);
+
+       PRINT_INFO(net, INIT_DBG, "Freeing wiphy\n");
+       wiphy_free(net->ieee80211_ptr->wiphy);
+       kfree(net->ieee80211_ptr);
+}
diff --git a/drivers/net/wireless/mchp/wilc_wfi_cfgoperations.h b/drivers/net/wireless/mchp/wilc_wfi_cfgoperations.h
new file mode 100644 (file)
index 0000000..9c56c4d
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef NM_WFI_CFGOPERATIONS
+#define NM_WFI_CFGOPERATIONS
+#include "wilc_wfi_netdevice.h"
+
+struct wireless_dev *wilc_create_wiphy(struct net_device *net,
+                                      struct device *dev);
+void wilc_free_wiphy(struct net_device *net);
+void wilc_deinit_host_int(struct net_device *net);
+int wilc_init_host_int(struct net_device *net);
+void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size);
+void wilc_wfi_deinit_mon_interface(struct wilc *wl);
+struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
+                                              const char *name,
+                                              struct net_device *real_dev);
+void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
+                             u16 frame_type, bool reg);
+void wilc_sysfs_init(struct wilc_vif *vif1, struct wilc_vif *vif2);
+void wilc_sysfs_exit(void);
+#endif
diff --git a/drivers/net/wireless/mchp/wilc_wfi_netdevice.h b/drivers/net/wireless/mchp/wilc_wfi_netdevice.h
new file mode 100644 (file)
index 0000000..0380c6d
--- /dev/null
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WFI_NETDEVICE
+#define WILC_WFI_NETDEVICE
+
+#include <linux/tcp.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/if_arp.h>
+#include <linux/version.h>
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+#include <linux/gpio/consumer.h>
+#else
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#endif
+
+#include "host_interface.h"
+#include "wilc_wlan.h"
+#include "wilc_wlan_cfg.h"
+
+#define FLOW_CTRL_LOW_THRESHLD         128
+#define FLOW_CTRL_UP_THRESHLD          256
+
+#define WILC_MAX_NUM_PMKIDS                    16
+#define PMKID_FOUND                            1
+#define NUM_STA_ASSOCIATED                     8
+
+#define NUM_REG_FRAME                          2
+
+#define TCP_ACK_FILTER_LINK_SPEED_THRESH       54
+#define DEFAULT_LINK_SPEED                     72
+
+#define GET_PKT_OFFSET(a) (((a) >> 22) & 0x1ff)
+
+#define ANT_SWTCH_INVALID_GPIO_CTRL            0
+#define ANT_SWTCH_SNGL_GPIO_CTRL               1
+#define ANT_SWTCH_DUAL_GPIO_CTRL               2
+
+struct wilc_wfi_stats {
+       unsigned long rx_packets;
+       unsigned long tx_packets;
+       unsigned long rx_bytes;
+       unsigned long tx_bytes;
+       u64 rx_time;
+       u64 tx_time;
+};
+
+struct wilc_wfi_key {
+       u8 *key;
+       u8 *seq;
+       int key_len;
+       int seq_len;
+       u32 cipher;
+};
+
+struct wilc_wfi_wep_key {
+       u8 *key;
+       u8 key_len;
+       u8 key_idx;
+};
+
+struct sta_info {
+       u8 sta_associated_bss[WILC_MAX_NUM_STA][ETH_ALEN];
+};
+
+/*Parameters needed for host interface for  remaining on channel*/
+struct wilc_wfi_p2p_listen_params {
+       struct ieee80211_channel *listen_ch;
+       u32 listen_duration;
+       u64 listen_cookie;
+};
+
+/* Struct to buffer eapol 1/4 frame */
+struct wilc_buffered_eap {
+       unsigned int size;
+       unsigned int pkt_offset;
+       u8 *buff;
+};
+
+struct wilc_p2p_var {
+       u8 local_random;
+       u8 recv_random;
+       bool is_wilc_ie;
+};
+
+static const u32 wilc_cipher_suites[] = {
+       WLAN_CIPHER_SUITE_WEP40,
+       WLAN_CIPHER_SUITE_WEP104,
+       WLAN_CIPHER_SUITE_TKIP,
+       WLAN_CIPHER_SUITE_CCMP,
+       WLAN_CIPHER_SUITE_AES_CMAC
+};
+
+#if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE
+#define CHAN2G(_channel, _freq, _flags) {       \
+       .band             = IEEE80211_BAND_2GHZ, \
+       .center_freq      = (_freq),             \
+       .hw_value         = (_channel),          \
+       .flags            = (_flags),            \
+       .max_antenna_gain = 0,                   \
+       .max_power        = 30,                  \
+}
+#else
+#define CHAN2G(_channel, _freq, _flags) {       \
+       .band             = NL80211_BAND_2GHZ, \
+       .center_freq      = (_freq),             \
+       .hw_value         = (_channel),          \
+       .flags            = (_flags),            \
+       .max_antenna_gain = 0,                   \
+       .max_power        = 30,                  \
+}
+#endif
+
+static const struct ieee80211_channel wilc_2ghz_channels[] = {
+       CHAN2G(1,  2412, 0),
+       CHAN2G(2,  2417, 0),
+       CHAN2G(3,  2422, 0),
+       CHAN2G(4,  2427, 0),
+       CHAN2G(5,  2432, 0),
+       CHAN2G(6,  2437, 0),
+       CHAN2G(7,  2442, 0),
+       CHAN2G(8,  2447, 0),
+       CHAN2G(9,  2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0)
+};
+
+#define RATETAB_ENT(_rate, _hw_value, _flags) {        \
+       .bitrate  = (_rate),                    \
+       .hw_value = (_hw_value),                \
+       .flags    = (_flags),                   \
+}
+
+static struct ieee80211_rate wilc_bitrates[] = {
+       RATETAB_ENT(10,  0,  0),
+       RATETAB_ENT(20,  1,  0),
+       RATETAB_ENT(55,  2,  0),
+       RATETAB_ENT(110, 3,  0),
+       RATETAB_ENT(60,  9,  0),
+       RATETAB_ENT(90,  6,  0),
+       RATETAB_ENT(120, 7,  0),
+       RATETAB_ENT(180, 8,  0),
+       RATETAB_ENT(240, 9,  0),
+       RATETAB_ENT(360, 10, 0),
+       RATETAB_ENT(480, 11, 0),
+       RATETAB_ENT(540, 12, 0)
+};
+
+struct wilc_priv {
+       struct wireless_dev *wdev;
+       struct cfg80211_scan_request *scan_req;
+       struct wilc_wfi_p2p_listen_params remain_on_ch_params;
+       u64 tx_cookie;
+       bool cfg_scanning;
+       u8 associated_bss[ETH_ALEN];
+       struct sta_info assoc_stainfo;
+       struct sk_buff *skb;
+       struct net_device *dev;
+       struct host_if_drv *hif_drv;
+       struct wilc_pmkid_attr pmkid_list;
+       u8 wep_key[4][WLAN_KEY_LEN_WEP104];
+       u8 wep_key_len[4];
+       /* The real interface that the monitor is on */
+       struct net_device *real_ndev;
+       struct wilc_wfi_key *wilc_gtk[WILC_MAX_NUM_STA];
+       struct wilc_wfi_key *wilc_ptk[WILC_MAX_NUM_STA];
+       u8 wilc_groupkey;
+
+       struct mutex scan_req_lock;
+
+       bool p2p_listen_state;
+       struct wilc_buffered_eap *buffered_eap;
+
+       struct timer_list eap_buff_timer;
+       int scanned_cnt;
+       struct wilc_p2p_var p2p;
+       struct ieee80211_channel channels[ARRAY_SIZE(wilc_2ghz_channels)];
+       struct ieee80211_rate bitrates[ARRAY_SIZE(wilc_bitrates)];
+       struct ieee80211_supported_band band;
+       u32 cipher_suites[ARRAY_SIZE(wilc_cipher_suites)];
+       u64 inc_roc_cookie;
+};
+
+struct frame_reg {
+       u16 type;
+       bool reg;
+};
+
+#define MAX_TCP_SESSION                25
+#define MAX_PENDING_ACKS               256
+
+struct ack_session_info {
+       u32 seq_num;
+       u32 bigger_ack_num;
+       u16 src_port;
+       u16 dst_port;
+       u16 status;
+};
+
+struct pending_acks {
+       u32 ack_num;
+       u32 session_index;
+       struct txq_entry_t  *txqe;
+};
+
+struct tcp_ack_filter {
+       struct ack_session_info ack_session_info[2 * MAX_TCP_SESSION];
+       struct pending_acks pending_acks[MAX_PENDING_ACKS];
+       u32 pending_base;
+       u32 tcp_session;
+       u32 pending_acks_idx;
+       bool enabled;
+};
+
+struct sysfs_attr_group {
+       bool p2p_mode;
+       u8 ant_swtch_mode;
+       u8 antenna1;
+       u8 antenna2;
+};
+
+struct wilc_vif {
+       u8 idx;
+       u8 iftype;
+       int monitor_flag;
+       int mac_opened;
+       struct frame_reg frame_reg[NUM_REG_FRAME];
+       struct net_device_stats netstats;
+       struct wilc *wilc;
+       u8 bssid[ETH_ALEN];
+       struct host_if_drv *hif_drv;
+       struct net_device *ndev;
+       u8 ifc_id;
+
+       struct sysfs_attr_group attr_sysfs;
+#ifdef DISABLE_PWRSAVE_AND_SCAN_DURING_IP
+       bool pwrsave_current_state;
+       struct timer_list during_ip_timer;
+       bool obtaining_ip;
+#endif
+       struct rf_info periodic_stats;
+       struct timer_list periodic_rssi;
+       struct tcp_ack_filter ack_filter;
+       bool connecting;
+};
+
+struct wilc {
+       const struct wilc_hif_func *hif_func;
+       int io_type;
+       s8 mac_status;
+#if KERNEL_VERSION(3, 13, 0) < LINUX_VERSION_CODE
+       struct gpio_desc *gpio_irq;
+#else
+       int gpio_irq;
+#endif
+       bool initialized;
+       int dev_irq_num;
+       int close;
+       u8 vif_num;
+       struct wilc_vif *vif[WILC_NUM_CONCURRENT_IFC];
+       u8 open_ifcs;
+       /*protect head of transmit queue*/
+       struct mutex txq_add_to_head_cs;
+       /*protect txq_entry_t transmit queue*/
+       spinlock_t txq_spinlock;
+       /*protect rxq_entry_t receiver queue*/
+       struct mutex rxq_cs;
+       /* lock to protect hif access */
+       struct mutex hif_cs;
+
+       struct completion cfg_event;
+       struct completion sync_event;
+       struct completion txq_event;
+       struct completion txq_thread_started;
+       struct completion debug_thread_started;
+       struct task_struct *txq_thread;
+       struct task_struct *debug_thread;
+
+       int quit;
+       /* lock to protect issue of wid command to fw */
+       struct mutex cfg_cmd_lock;
+       struct wilc_cfg_frame cfg_frame;
+       u32 cfg_frame_offset;
+       u8 cfg_seq_no;
+
+       u8 *rx_buffer;
+       u32 rx_buffer_offset;
+       u8 *tx_buffer;
+
+       struct txq_handle txq[NQUEUES];
+       int txq_entries;
+
+       struct rxq_entry_t rxq_head;
+
+       const struct firmware *firmware;
+
+       struct device *dev;
+       struct device *dt_dev;
+
+       enum wilc_chip_type chip;
+
+       uint8_t power_status[DEV_MAX];
+       uint8_t keep_awake[DEV_MAX];
+       struct mutex cs;
+       int clients_count;
+       struct workqueue_struct *hif_workqueue;
+
+       struct wilc_cfg cfg;
+       void *bus_data;
+       struct net_device *monitor_dev;
+       /* deinit lock */
+       struct mutex deinit_lock;
+       u8 sta_ch;
+       u8 op_ch;
+};
+
+struct wilc_wfi_mon_priv {
+       struct net_device *real_ndev;
+};
+
+void wilc_frmw_to_host(struct wilc_vif *vif, u8 *buff, u32 size,
+                      u32 pkt_offset, u8 status);
+void wilc_mac_indicate(struct wilc *wilc);
+void wilc_netdev_cleanup(struct wilc *wilc);
+int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type,
+                    const struct wilc_hif_func *ops);
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size);
+void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode);
+
+#endif
diff --git a/drivers/net/wireless/mchp/wilc_wlan.c b/drivers/net/wireless/mchp/wilc_wlan.c
new file mode 100644 (file)
index 0000000..2d6e261
--- /dev/null
@@ -0,0 +1,2031 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include "wilc_wfi_netdevice.h"
+#include "wilc_wlan_cfg.h"
+#include "wilc_netdev.h"
+
+#define WAKUP_TRAILS_TIMEOUT           (10000)
+
+#if KERNEL_VERSION(3, 12, 21) > LINUX_VERSION_CODE
+#define list_next_entry(pos, member) \
+       list_entry((pos)->member.next, typeof(*(pos)), member)
+#endif
+
+void acquire_bus(struct wilc *wilc, enum bus_acquire acquire, int source)
+{
+       mutex_lock(&wilc->hif_cs);
+       if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP)
+               chip_wakeup(wilc, source);
+}
+
+void release_bus(struct wilc *wilc, enum bus_release release, int source)
+{
+       if (release == WILC_BUS_RELEASE_ALLOW_SLEEP)
+               chip_allow_sleep(wilc, source);
+       mutex_unlock(&wilc->hif_cs);
+}
+
+uint8_t reset_bus(struct wilc *wilc)
+{
+       uint8_t ret = 0;
+
+       if (wilc->io_type == WILC_HIF_SPI)
+               return wilc->hif_func->hif_reset(wilc);
+       return ret;
+}
+
+static void wilc_wlan_txq_remove(struct wilc *wilc, u8 q_num,
+                                struct txq_entry_t *tqe)
+{
+       list_del(&tqe->list);
+       wilc->txq_entries -= 1;
+       wilc->txq[q_num].count--;
+}
+
+static struct txq_entry_t *
+wilc_wlan_txq_remove_from_head(struct net_device *dev, u8 q_num)
+{
+       struct txq_entry_t *tqe = NULL;
+       unsigned long flags;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       if (!list_empty(&wilc->txq[q_num].txq_head.list)) {
+               tqe = list_first_entry(&wilc->txq[q_num].txq_head.list,
+                                      struct txq_entry_t, list);
+               list_del(&tqe->list);
+               wilc->txq_entries -= 1;
+               wilc->txq[q_num].count--;
+       }
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+       return tqe;
+}
+
+static void wilc_wlan_txq_add_to_tail(struct net_device *dev, u8 q_num,
+                                     struct txq_entry_t *tqe)
+{
+       unsigned long flags;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       list_add_tail(&tqe->list, &wilc->txq[q_num].txq_head.list);
+       wilc->txq_entries += 1;
+       wilc->txq[q_num].count++;
+       PRINT_INFO(vif->ndev, TX_DBG, "Number of entries in TxQ = %d\n",
+                  wilc->txq_entries);
+
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+       PRINT_INFO(vif->ndev, TX_DBG, "Wake the txq_handling\n");
+       complete(&wilc->txq_event);
+}
+
+static void wilc_wlan_txq_add_to_head(struct wilc_vif *vif, u8 q_num,
+                                    struct txq_entry_t *tqe)
+{
+       unsigned long flags;
+       struct wilc *wilc = vif->wilc;
+
+       mutex_lock(&wilc->txq_add_to_head_cs);
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       list_add(&tqe->list, &wilc->txq[q_num].txq_head.list);
+       wilc->txq_entries += 1;
+       wilc->txq[q_num].count++;
+       PRINT_INFO(vif->ndev, TX_DBG, "Number of entries in TxQ = %d\n",
+                  wilc->txq_entries);
+
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+       mutex_unlock(&wilc->txq_add_to_head_cs);
+       complete(&wilc->txq_event);
+       PRINT_INFO(vif->ndev, TX_DBG, "Wake up the txq_handler\n");
+}
+
+#define NOT_TCP_ACK                    (-1)
+
+static inline void add_tcp_session(struct wilc_vif *vif, u32 src_prt,
+                                 u32 dst_prt, u32 seq)
+{
+       struct tcp_ack_filter *f = &vif->ack_filter;
+
+       if (f->tcp_session < 2 * MAX_TCP_SESSION) {
+               f->ack_session_info[f->tcp_session].seq_num = seq;
+               f->ack_session_info[f->tcp_session].bigger_ack_num = 0;
+               f->ack_session_info[f->tcp_session].src_port = src_prt;
+               f->ack_session_info[f->tcp_session].dst_port = dst_prt;
+               f->tcp_session++;
+               PRINT_INFO(vif->ndev, TCP_ENH, "TCP Session %d to Ack %d\n",
+                          f->tcp_session, seq);
+       }
+}
+
+static inline void update_tcp_session(struct wilc_vif *vif, u32 index, u32 ack)
+{
+       struct tcp_ack_filter *f = &vif->ack_filter;
+
+       if (index < 2 * MAX_TCP_SESSION &&
+           ack > f->ack_session_info[index].bigger_ack_num)
+               f->ack_session_info[index].bigger_ack_num = ack;
+}
+
+static inline void add_tcp_pending_ack(struct wilc_vif *vif, u32 ack,
+                                     u32 session_index,
+                                     struct txq_entry_t *txqe)
+{
+       struct tcp_ack_filter *f = &vif->ack_filter;
+       u32 i = f->pending_base + f->pending_acks_idx;
+
+       if (i < MAX_PENDING_ACKS) {
+               f->pending_acks[i].ack_num = ack;
+               f->pending_acks[i].txqe = txqe;
+               f->pending_acks[i].session_index = session_index;
+               txqe->ack_idx = i;
+               f->pending_acks_idx++;
+       }
+}
+
+static inline void tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
+{
+       void *buffer = tqe->buffer;
+       const struct ethhdr *eth_hdr_ptr = buffer;
+       int i;
+       unsigned long flags;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       struct tcp_ack_filter *f = &vif->ack_filter;
+       const struct iphdr *ip_hdr_ptr;
+       const struct tcphdr *tcp_hdr_ptr;
+       u32 ihl, total_length, data_offset;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       if (eth_hdr_ptr->h_proto != htons(ETH_P_IP))
+               goto out;
+
+       ip_hdr_ptr = buffer + ETH_HLEN;
+
+       if (ip_hdr_ptr->protocol != IPPROTO_TCP)
+               goto out;
+
+       ihl = ip_hdr_ptr->ihl << 2;
+       tcp_hdr_ptr = buffer + ETH_HLEN + ihl;
+       total_length = ntohs(ip_hdr_ptr->tot_len);
+
+       data_offset = tcp_hdr_ptr->doff << 2;
+       if (total_length == (ihl + data_offset)) {
+               u32 seq_no, ack_no;
+
+               seq_no = ntohl(tcp_hdr_ptr->seq);
+               ack_no = ntohl(tcp_hdr_ptr->ack_seq);
+               for (i = 0; i < f->tcp_session; i++) {
+                       u32 j = f->ack_session_info[i].seq_num;
+
+                       if (i < 2 * MAX_TCP_SESSION &&
+                           j == seq_no) {
+                               update_tcp_session(vif, i, ack_no);
+                               break;
+                       }
+               }
+               if (i == f->tcp_session)
+                       add_tcp_session(vif, 0, 0, seq_no);
+
+               add_tcp_pending_ack(vif, ack_no, i, tqe);
+       }
+
+out:
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+}
+
+static void wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev)
+{
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       struct tcp_ack_filter *f = &vif->ack_filter;
+       u32 i = 0;
+       u32 dropped = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+       for (i = f->pending_base;
+            i < (f->pending_base + f->pending_acks_idx); i++) {
+               u32 index;
+               u32 bigger_ack_num;
+
+               if (i >= MAX_PENDING_ACKS)
+                       break;
+
+               index = f->pending_acks[i].session_index;
+
+               if (index >= 2 * MAX_TCP_SESSION)
+                       break;
+
+               bigger_ack_num = f->ack_session_info[index].bigger_ack_num;
+
+               if (f->pending_acks[i].ack_num < bigger_ack_num) {
+                       struct txq_entry_t *tqe;
+
+                       PRINT_INFO(vif->ndev, TCP_ENH, "DROP ACK: %u\n",
+                                  f->pending_acks[i].ack_num);
+                       tqe = f->pending_acks[i].txqe;
+                       if (tqe) {
+                               wilc_wlan_txq_remove(wilc, tqe->q_num, tqe);
+                               tqe->status = 1;
+                               if (tqe->tx_complete_func)
+                                       tqe->tx_complete_func(tqe->priv,
+                                                             tqe->status);
+                               kfree(tqe);
+                               dropped++;
+                       }
+               }
+       }
+       f->pending_acks_idx = 0;
+       f->tcp_session = 0;
+
+       if (f->pending_base == 0)
+               f->pending_base = MAX_TCP_SESSION;
+       else
+               f->pending_base = 0;
+
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+       while (dropped > 0) {
+               if (!wait_for_completion_timeout(&wilc->txq_event,
+                                               msecs_to_jiffies(1)))
+                       PRINT_ER(vif->ndev, "completion timedout\n");
+               dropped--;
+       }
+}
+
+static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
+{
+       u8 *bssid, *bssid1;
+       int i = 0;
+       struct net_device *mon_netdev = NULL;
+
+       bssid = mac_header + 10;
+       bssid1 = mac_header + 4;
+       for (i = 0; i < wilc->vif_num; i++) {
+               if (wilc->vif[i]->iftype == WILC_STATION_MODE)
+                       if (ether_addr_equal_unaligned(bssid,
+                                                      wilc->vif[i]->bssid))
+                               return wilc->vif[i]->ndev;
+               if (wilc->vif[i]->iftype == WILC_AP_MODE)
+                       if (ether_addr_equal_unaligned(bssid1,
+                                                      wilc->vif[i]->bssid))
+                               return wilc->vif[i]->ndev;
+               if (wilc->vif[i]->iftype == WILC_MONITOR_MODE)
+                       mon_netdev = wilc->vif[i]->ndev;
+       }
+
+       if (!mon_netdev)
+               PRINT_WRN(wilc->vif[0]->ndev, GENERIC_DBG, "Invalid handle\n");
+       return mon_netdev;
+}
+
+void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value)
+{
+       vif->ack_filter.enabled = value;
+}
+
+static int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer,
+                                    u32 buffer_size)
+{
+       struct txq_entry_t *tqe;
+       struct wilc *wilc = vif->wilc;
+
+       PRINT_INFO(vif->ndev, TX_DBG, "Adding config packet ...\n");
+       if (wilc->quit) {
+               PRINT_INFO(vif->ndev, TX_DBG, "Return due to clear function\n");
+               complete(&wilc->cfg_event);
+               return 0;
+       }
+
+       if (!(wilc->initialized)) {
+               PRINT_INFO(vif->ndev, TX_DBG, "wilc not initialized\n");
+               complete(&wilc->cfg_event);
+               return 0;
+       }
+       tqe = kmalloc(sizeof(*tqe), GFP_KERNEL);
+       if (!tqe) {
+               complete(&wilc->cfg_event);
+               return 0;
+       }
+       tqe->type = WILC_CFG_PKT;
+       tqe->buffer = buffer;
+       tqe->buffer_size = buffer_size;
+       tqe->tx_complete_func = NULL;
+       tqe->priv = NULL;
+       tqe->q_num = AC_VO_Q;
+       tqe->ack_idx = NOT_TCP_ACK;
+
+       PRINT_INFO(vif->ndev, TX_DBG,
+                  "Adding the config packet at the Queue tail\n");
+
+       wilc_wlan_txq_add_to_head(vif, AC_VO_Q, tqe);
+
+       return 1;
+}
+
+static void ac_q_limit(struct wilc *wilc, u8 ac, u16 *q_limit)
+{
+       static u8 buffer[AC_BUFFER_SIZE];
+       static u16 end_index;
+       static bool initialized;
+       static u16 cnt[NQUEUES];
+       u8 factors[NQUEUES] = {1, 1, 1, 1};
+       static u16 sum;
+       u16 i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+       if (!initialized) {
+               for (i = 0; i < AC_BUFFER_SIZE; i++)
+                       buffer[i] = i % NQUEUES;
+
+               for (i = 0; i < NQUEUES; i++) {
+                       cnt[i] = AC_BUFFER_SIZE * factors[i] / NQUEUES;
+                       sum += cnt[i];
+               }
+               end_index = AC_BUFFER_SIZE - 1;
+               initialized = 1;
+       }
+
+       cnt[buffer[end_index]] -= factors[buffer[end_index]];
+       cnt[ac] += factors[ac];
+       sum += (factors[ac] - factors[buffer[end_index]]);
+
+       buffer[end_index] = ac;
+       if (end_index > 0)
+               end_index--;
+       else
+               end_index = AC_BUFFER_SIZE - 1;
+
+       for (i = 0; i < NQUEUES; i++) {
+               if (!sum)
+                       q_limit[i] = 1;
+               else
+                       q_limit[i] = (cnt[i] * FLOW_CTRL_UP_THRESHLD / sum) + 1;
+       }
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+}
+
+static inline u8 ac_classify(struct wilc *wilc, struct txq_entry_t *tqe)
+{
+       u8 *eth_hdr_ptr;
+       u8 *buffer = tqe->buffer;
+       u8 ac;
+       u16 h_proto;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       eth_hdr_ptr = &buffer[0];
+       h_proto = ntohs(*((unsigned short *)&eth_hdr_ptr[12]));
+       if (h_proto == ETH_P_IP) {
+               u8 *ip_hdr_ptr;
+               u32 IHL, DSCP;
+
+               ip_hdr_ptr = &buffer[ETHERNET_HDR_LEN];
+               IHL = (ip_hdr_ptr[0] & 0xf) << 2;
+               DSCP = (ip_hdr_ptr[1] & 0xfc);
+
+               switch (DSCP) {
+               case 0x20:
+               case 0x40:
+               case 0x08:
+                       ac = AC_BK_Q;
+                       break;
+               case 0x80:
+               case 0xA0:
+               case 0x28:
+                       ac = AC_VI_Q;
+                       break;
+               case 0xC0:
+               case 0xd0:
+               case 0xE0:
+               case 0x88:
+               case 0xB8:
+                       ac = AC_VO_Q;
+                       break;
+               default:
+                       ac = AC_BE_Q;
+                       break;
+               }
+       } else {
+               ac  = AC_BE_Q;
+       }
+
+       tqe->q_num = ac;
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+       return ac;
+}
+
+static inline int ac_balance(u8 *count, u8 *ratio)
+{
+       u8 i, max_count = 0;
+
+       if (!count || !ratio)
+               return -1;
+
+       for (i = 0; i < NQUEUES; i++)
+               if (count[i] > max_count)
+                       max_count = count[i];
+
+       for (i = 0; i < NQUEUES; i++)
+               ratio[i] = max_count - count[i];
+
+       return 0;
+}
+
+static inline void ac_pkt_count(u32 reg, u8 *pkt_count)
+{
+       pkt_count[AC_BK_Q] = (reg & 0x000000fa) >> BK_AC_COUNT_POS;
+       pkt_count[AC_BE_Q] = (reg & 0x0000fe00) >> BE_AC_COUNT_POS;
+       pkt_count[AC_VI_Q] = (reg & 0x00fe0000) >> VI_AC_COUNT_POS;
+       pkt_count[AC_VO_Q] = (reg & 0xfe000000) >> VO_AC_COUNT_POS;
+}
+
+static inline u8 ac_change(struct wilc *wilc, u8 *ac)
+{
+       do {
+               if (wilc->txq[*ac].acm == 0)
+                       return 0;
+               (*ac)++;
+       } while (*ac < NQUEUES);
+       return 1;
+}
+
+static inline void ac_acm_bit(struct wilc *wilc, u32 reg)
+{
+       wilc->txq[AC_BK_Q].acm = (reg & 0x00000002) >> BK_AC_ACM_STAT_POS;
+       wilc->txq[AC_BE_Q].acm = (reg & 0x00000100) >> BE_AC_ACM_STAT_POS;
+       wilc->txq[AC_VI_Q].acm = (reg & 0x00010000) >> VI_AC_ACM_STAT_POS;
+       wilc->txq[AC_VO_Q].acm = (reg & 0x01000000) >> VO_AC_ACM_STAT_POS;
+}
+
+int txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
+                             u32 buffer_size,
+                             void (*tx_complete_fn)(void *, int))
+{
+       struct txq_entry_t *tqe;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc;
+       u8 q_num;
+       u16 q_limit[NQUEUES] = {0, 0, 0, 0};
+
+       if (!vif) {
+               pr_info("%s vif is NULL\n", __func__);
+               return -1;
+       }
+
+       wilc = vif->wilc;
+
+       if (wilc->quit) {
+               PRINT_INFO(vif->ndev, TX_DBG,
+                          "drv is quitting, return from net_pkt\n");
+               tx_complete_fn(priv, 0);
+               return 0;
+       }
+
+       if (!(wilc->initialized)) {
+               PRINT_INFO(vif->ndev, TX_DBG,
+                          "not_init, return from net_pkt\n");
+               tx_complete_fn(priv, 0);
+               return 0;
+       }
+
+       tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
+
+       if (!tqe) {
+               PRINT_INFO(vif->ndev, TX_DBG,
+                          "malloc failed, return from net_pkt\n");
+               tx_complete_fn(priv, 0);
+               return 0;
+       }
+       tqe->type = WILC_NET_PKT;
+       tqe->buffer = buffer;
+       tqe->buffer_size = buffer_size;
+       tqe->tx_complete_func = tx_complete_fn;
+       tqe->priv = priv;
+
+       q_num = ac_classify(wilc, tqe);
+       if (ac_change(wilc, &q_num)) {
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "No suitable non-ACM queue\n");
+               kfree(tqe);
+               return 0;
+       }
+       ac_q_limit(wilc, q_num, q_limit);
+
+       if ((q_num == AC_VO_Q && wilc->txq[q_num].count <= q_limit[AC_VO_Q]) ||
+           (q_num == AC_VI_Q && wilc->txq[q_num].count <= q_limit[AC_VI_Q]) ||
+           (q_num == AC_BE_Q && wilc->txq[q_num].count <= q_limit[AC_BE_Q]) ||
+           (q_num == AC_BK_Q && wilc->txq[q_num].count <= q_limit[AC_BK_Q])) {
+               PRINT_INFO(vif->ndev, TX_DBG,
+                          "Adding mgmt packet at the Queue tail\n");
+               tqe->ack_idx = NOT_TCP_ACK;
+               if (vif->ack_filter.enabled)
+                       tcp_process(dev, tqe);
+               wilc_wlan_txq_add_to_tail(dev, q_num, tqe);
+       } else {
+               tqe->status = 0;
+               if (tqe->tx_complete_func)
+                       tqe->tx_complete_func(tqe->priv, tqe->status);
+               kfree(tqe);
+       }
+
+       return wilc->txq_entries;
+}
+
+int txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+                              u32 buffer_size,
+                              void (*tx_complete_fn)(void *, int))
+{
+       struct txq_entry_t *tqe;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc;
+
+       wilc = vif->wilc;
+
+       if (wilc->quit) {
+               PRINT_INFO(vif->ndev, TX_DBG, "drv is quitting\n");
+               tx_complete_fn(priv, 0);
+               return 0;
+       }
+
+       if (!(wilc->initialized)) {
+               PRINT_INFO(vif->ndev, TX_DBG, "wilc not_init\n");
+               tx_complete_fn(priv, 0);
+               return 0;
+       }
+       tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
+
+       if (!tqe) {
+               PRINT_INFO(vif->ndev, TX_DBG, "Queue malloc failed\n");
+               tx_complete_fn(priv, 0);
+               return 0;
+       }
+       tqe->type = WILC_MGMT_PKT;
+       tqe->buffer = buffer;
+       tqe->buffer_size = buffer_size;
+       tqe->tx_complete_func = tx_complete_fn;
+       tqe->priv = priv;
+       tqe->q_num = AC_BE_Q;
+       tqe->ack_idx = NOT_TCP_ACK;
+
+       PRINT_INFO(vif->ndev, TX_DBG, "Adding Mgmt packet to Queue tail\n");
+       wilc_wlan_txq_add_to_tail(dev, AC_VO_Q, tqe);
+       return 1;
+}
+
+static struct txq_entry_t *txq_get_first(struct wilc *wilc, u8 q_num)
+{
+       struct txq_entry_t *tqe = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       if (!list_empty(&wilc->txq[q_num].txq_head.list))
+               tqe = list_first_entry(&wilc->txq[q_num].txq_head.list,
+                                      struct txq_entry_t, list);
+
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+       return tqe;
+}
+
+static struct txq_entry_t *txq_get_next(struct wilc *wilc,
+                                       struct txq_entry_t *tqe, u8 q_num)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+       if (!list_is_last(&tqe->list, &wilc->txq[q_num].txq_head.list))
+               tqe = list_next_entry(tqe, list);
+       else
+               tqe = NULL;
+       spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+       return tqe;
+}
+
+static void rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe)
+{
+       struct wilc_vif *vif = wilc->vif[0];
+
+       if (wilc->quit)
+               return;
+
+       mutex_lock(&wilc->rxq_cs);
+       list_add_tail(&rqe->list, &wilc->rxq_head.list);
+       PRINT_INFO(vif->ndev, RX_DBG, "Added to RX queue\n");
+       mutex_unlock(&wilc->rxq_cs);
+}
+
+static struct rxq_entry_t *rxq_remove(struct wilc *wilc)
+{
+       struct wilc_vif *vif = wilc->vif[0];
+       struct rxq_entry_t *rqe = NULL;
+
+       PRINT_INFO(vif->ndev, RX_DBG, "Getting rxQ element\n");
+
+       mutex_lock(&wilc->rxq_cs);
+       if (!list_empty(&wilc->rxq_head.list)) {
+               rqe = list_first_entry(&wilc->rxq_head.list, struct rxq_entry_t,
+                                      list);
+               list_del(&rqe->list);
+       } else {
+               PRINT_INFO(vif->ndev, RX_DBG, "Nothing to get from Q\n");
+       }
+       mutex_unlock(&wilc->rxq_cs);
+       return rqe;
+}
+
+static int chip_allow_sleep_wilc1000(struct wilc *wilc, int source)
+{
+       u32 reg = 0;
+       const struct wilc_hif_func *hif_func = wilc->hif_func;
+       u32 wakeup_reg, wakeup_bit;
+       u32 to_host_from_fw_reg, to_host_from_fw_bit;
+       u32 from_host_to_fw_reg, from_host_to_fw_bit;
+       u32 trials = 100;
+       int ret;
+
+       if (wilc->io_type == WILC_HIF_SDIO ||
+               wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+               wakeup_reg = 0xf0;
+               wakeup_bit = BIT(0);
+               from_host_to_fw_reg = 0xfa;
+               from_host_to_fw_bit = BIT(0);
+               to_host_from_fw_reg = 0xfc;
+               to_host_from_fw_bit = BIT(0);
+       } else {
+               wakeup_reg = 0x1;
+               wakeup_bit = BIT(1);
+               from_host_to_fw_reg = 0x0b;
+               from_host_to_fw_bit = BIT(0);
+               to_host_from_fw_reg = 0xfc;
+               to_host_from_fw_bit = BIT(0);
+       }
+
+       while (trials--) {
+               ret = hif_func->hif_read_reg(wilc, to_host_from_fw_reg, &reg);
+               if (!ret)
+                       return -EIO;
+               if ((reg & to_host_from_fw_bit) == 0)
+                       break;
+       }
+       if (!trials)
+               pr_warn("FW not responding\n");
+
+       /* Clear bit 1 */
+       ret = hif_func->hif_read_reg(wilc, wakeup_reg, &reg);
+       if (!ret)
+               return -EIO;
+       if (reg & wakeup_bit) {
+               reg &= ~wakeup_bit;
+               ret = hif_func->hif_write_reg(wilc, wakeup_reg, reg);
+               if (!ret)
+                       return -EIO;
+       }
+
+       ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, &reg);
+       if (!ret)
+               return -EIO;
+       if (reg & from_host_to_fw_bit) {
+               reg &= ~from_host_to_fw_bit;
+               ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg, reg);
+               if (!ret)
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static int chip_allow_sleep_wilc3000(struct wilc *wilc, int source)
+{
+       u32 reg = 0;
+       int ret;
+       const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+       if (wilc->io_type == WILC_HIF_SDIO ||
+               wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+               ret = hif_func->hif_read_reg(wilc, 0xf0, &reg);
+               if (!ret)
+                       return -EIO;
+               ret = hif_func->hif_write_reg(wilc, 0xf0, reg & ~BIT(0));
+               if (!ret)
+                       return -EIO;
+       } else {
+               ret = hif_func->hif_read_reg(wilc, 0x1, &reg);
+               if (!ret)
+                       return -EIO;
+               ret = hif_func->hif_write_reg(wilc, 0x1, reg & ~BIT(1));
+               if (!ret)
+                       return -EIO;
+       }
+       return 0;
+}
+
+void chip_allow_sleep(struct wilc *wilc, int source)
+{
+       int ret = 0;
+
+       if (((source == DEV_WIFI) && (wilc->keep_awake[DEV_BT] == true)) ||
+           ((source == DEV_BT) && (wilc->keep_awake[DEV_WIFI] == true)))
+               pr_warn("Another device is preventing allow sleep operation. request source is %s\n",
+                         (source == DEV_WIFI ? "Wifi" : "BT"));
+       else
+               if (wilc->chip == WILC_1000)
+                       ret = chip_allow_sleep_wilc1000(wilc, source);
+               else
+                       ret = chip_allow_sleep_wilc3000(wilc, source);
+       if (!ret)
+               wilc->keep_awake[source] = false;
+}
+
+void chip_wakeup_wilc1000(struct wilc *wilc, int source)
+{
+       u32 ret = 0;
+       u32 reg = 0, clk_status_val = 0, trials = 0;
+       u32 wakeup_reg, wakeup_bit;
+       u32 clk_status_reg, clk_status_bit;
+       u32 to_host_from_fw_reg, to_host_from_fw_bit;
+       u32 from_host_to_fw_reg, from_host_to_fw_bit;
+       const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+       if (wilc->io_type == WILC_HIF_SDIO ||
+               wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+               wakeup_reg = 0xf0;
+               clk_status_reg = 0xf1;
+               wakeup_bit = BIT(0);
+               clk_status_bit = BIT(0);
+               from_host_to_fw_reg = 0xfa;
+               from_host_to_fw_bit = BIT(0);
+               to_host_from_fw_reg = 0xfc;
+               to_host_from_fw_bit = BIT(0);
+       } else {
+               wakeup_reg = 0x1;
+               clk_status_reg = 0x0f;
+               wakeup_bit = BIT(1);
+               clk_status_bit = BIT(2);
+               from_host_to_fw_reg = 0x0b;
+               from_host_to_fw_bit = BIT(0);
+               to_host_from_fw_reg = 0xfc;
+               to_host_from_fw_bit = BIT(0);
+       }
+
+       ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, &reg);
+       if (!ret)
+               goto _fail_;
+
+       if (!(reg & from_host_to_fw_bit)) {
+               /*USE bit 0 to indicate host wakeup*/
+               ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg,
+                                             reg | from_host_to_fw_bit);
+               if (!ret)
+                       goto _fail_;
+       }
+
+       ret = hif_func->hif_read_reg(wilc, wakeup_reg, &reg);
+       if (!ret)
+               goto _fail_;
+       /* Set bit 1 */
+       if (!(reg & wakeup_bit)) {
+               ret = hif_func->hif_write_reg(wilc, wakeup_reg,
+                                             reg | wakeup_bit);
+               if (!ret)
+                       goto _fail_;
+       }
+
+       do {
+               ret = hif_func->hif_read_reg(wilc, clk_status_reg,
+                                            &clk_status_val);
+               if (!ret) {
+                       pr_err("Bus error (5).%d %x\n", ret, clk_status_val);
+                       goto _fail_;
+               }
+               if (clk_status_val & clk_status_bit)
+                       break;
+
+               //nm_bsp_sleep(2);
+               trials++;
+               if (trials > WAKUP_TRAILS_TIMEOUT) {
+                       pr_err("Failed to wakup the chip\n");
+                       ret = -1;
+                       goto _fail_;
+               }
+       } while (1);
+
+       if (wilc_get_chipid(wilc, false) < 0x1002b0) {
+               uint32_t val32;
+               /* Enable PALDO back right after wakeup */
+               hif_func->hif_read_reg(wilc, 0x1e1c, &val32);
+               val32 |= BIT(6);
+               hif_func->hif_write_reg(wilc, 0x1e1c, val32);
+
+               hif_func->hif_read_reg(wilc, 0x1e9c, &val32);
+               val32 |= BIT(6);
+               hif_func->hif_write_reg(wilc, 0x1e9c, val32);
+       }
+       /*workaround sometimes spi fail to read clock regs after reading
+        * writing clockless registers
+        */
+       reset_bus(wilc);
+
+_fail_:
+       return;
+}
+
+void chip_wakeup_wilc3000(struct wilc *wilc, int source)
+{
+       u32 wakeup_reg_val, clk_status_reg_val, trials = 0;
+       u32 wakeup_reg, wakeup_bit;
+       u32 clk_status_reg, clk_status_bit;
+       int wake_seq_trials = 5;
+       const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+       if (wilc->io_type == WILC_HIF_SDIO ||
+               wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) {
+               wakeup_reg = 0xf0;
+               clk_status_reg = 0xf0;
+               wakeup_bit = BIT(0);
+               clk_status_bit = BIT(4);
+       } else {
+               wakeup_reg = 0x1;
+               clk_status_reg = 0x13;
+               wakeup_bit = BIT(1);
+               clk_status_bit = BIT(2);
+       }
+
+       hif_func->hif_read_reg(wilc, wakeup_reg, &wakeup_reg_val);
+       do {
+               hif_func->hif_write_reg(wilc, wakeup_reg, wakeup_reg_val |
+                                                         wakeup_bit);
+               /* Check the clock status */
+               hif_func->hif_read_reg(wilc, clk_status_reg,
+                                      &clk_status_reg_val);
+
+               /*
+                * in case of clocks off, wait 1ms, and check it again.
+                * if still off, wait for another 1ms, for a total wait of 3ms.
+                * If still off, redo the wake up sequence
+                */
+               while ((clk_status_reg_val & clk_status_bit) == 0 &&
+                      (++trials % 4) != 0) {
+                       /* Wait for the chip to stabilize*/
+                       usleep_range(1000, 1100);
+
+                       /*
+                        * Make sure chip is awake. This is an extra step that
+                        * can be removed later to avoid the bus access
+                        * overhead
+                        */
+                       hif_func->hif_read_reg(wilc, clk_status_reg,
+                                              &clk_status_reg_val);
+               }
+               /* in case of failure, Reset the wakeup bit to introduce a new
+                * edge on the next loop
+                */
+               if ((clk_status_reg_val & clk_status_bit) == 0)
+                       hif_func->hif_write_reg(wilc, wakeup_reg,
+                                               wakeup_reg_val & (~wakeup_bit));
+       } while (((clk_status_reg_val & clk_status_bit) == 0)
+                && (wake_seq_trials-- > 0));
+       if (!wake_seq_trials)
+               dev_err(wilc->dev, "clocks still OFF. Wake up failed\n");
+       wilc->keep_awake[source] = true;
+}
+
+void chip_wakeup(struct wilc *wilc, int source)
+{
+       if (wilc->chip == WILC_1000)
+               chip_wakeup_wilc1000(wilc, source);
+       else
+               chip_wakeup_wilc3000(wilc, source);
+}
+
+void host_wakeup_notify(struct wilc *wilc, int source)
+{
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source);
+       if (wilc->chip == WILC_1000)
+               wilc->hif_func->hif_write_reg(wilc, 0x10b0, 1);
+       else
+               wilc->hif_func->hif_write_reg(wilc, 0x10c0, 1);
+       release_bus(wilc, WILC_BUS_RELEASE_ONLY, source);
+}
+
+void host_sleep_notify(struct wilc *wilc, int source)
+{
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source);
+       if (wilc->chip == WILC_1000)
+               wilc->hif_func->hif_write_reg(wilc, 0x10ac, 1);
+       else
+               wilc->hif_func->hif_write_reg(wilc, 0x10bc, 1);
+       release_bus(wilc, WILC_BUS_RELEASE_ONLY, source);
+}
+
+static u8 ac_fw_count[NQUEUES] = {0, 0, 0, 0};
+int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count)
+{
+       int i, entries = 0;
+       u8 k, ac;
+       u32 sum;
+       u32 reg;
+       u8 ac_desired_ratio[NQUEUES] = {0, 0, 0, 0};
+       u8 ac_preserve_ratio[NQUEUES] = {1, 1, 1, 1};
+       u8 *num_pkts_to_add;
+       u8 vmm_entries_ac[WILC_VMM_TBL_SIZE];
+       u8 *txb;
+       u32 offset = 0;
+       bool max_size_over = 0, ac_exist = 0;
+       int vmm_sz = 0;
+       struct txq_entry_t *tqe_q[NQUEUES];
+       int ret = 0;
+       int counter;
+       int timeout;
+       u32 vmm_table[WILC_VMM_TBL_SIZE];
+       u8 ac_pkt_num_to_chip[NQUEUES] = {0, 0, 0, 0};
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+       const struct wilc_hif_func *func;
+
+       txb = wilc->tx_buffer;
+       if (!wilc->txq_entries) {
+               *txq_count = 0;
+               return 0;
+       }
+
+       if (wilc->quit)
+               goto out;
+       if (ac_balance(ac_fw_count, ac_desired_ratio))
+               return -1;
+
+       mutex_lock(&wilc->txq_add_to_head_cs);
+       wilc_wlan_txq_filter_dup_tcp_ack(dev);
+
+       PRINT_INFO(vif->ndev, TX_DBG, "Getting the head of the TxQ\n");
+       for (ac = 0; ac < NQUEUES; ac++)
+               tqe_q[ac] = txq_get_first(wilc, ac);
+       i = 0;
+       sum = 0;
+       max_size_over = 0;
+       num_pkts_to_add = ac_desired_ratio;
+       do {
+               ac_exist = 0;
+               for (ac = 0; (ac < NQUEUES) && (!max_size_over); ac++) {
+                       if (!tqe_q[ac])
+                               continue;
+
+                       ac_exist = 1;
+                       for (k = 0; (k < num_pkts_to_add[ac]) &&
+                                   (!max_size_over) && tqe_q[ac]; k++) {
+                               if (i >= (WILC_VMM_TBL_SIZE - 1)) {
+                                       max_size_over = 1;
+                                       break;
+                               }
+
+                               if (tqe_q[ac]->type == WILC_CFG_PKT)
+                                       vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
+                               else if (tqe_q[ac]->type == WILC_NET_PKT)
+                                       vmm_sz = ETH_ETHERNET_HDR_OFFSET;
+                               else
+                                       vmm_sz = HOST_HDR_OFFSET;
+
+                               vmm_sz += tqe_q[ac]->buffer_size;
+                               PRINT_INFO(vif->ndev, TX_DBG,
+                                          "VMM Size before alignment = %d\n",
+                                          vmm_sz);
+                               if (vmm_sz & 0x3)
+                                       vmm_sz = (vmm_sz + 4) & ~0x3;
+
+                               if ((sum + vmm_sz) > WILC_TX_BUFF_SIZE) {
+                                       max_size_over = 1;
+                                       break;
+                               }
+                               PRINT_INFO(vif->ndev, TX_DBG,
+                                          "VMM Size AFTER alignment = %d\n",
+                                          vmm_sz);
+                               vmm_table[i] = vmm_sz / 4;
+                               PRINT_INFO(vif->ndev, TX_DBG,
+                                          "VMMTable entry size = %d\n",
+                                          vmm_table[i]);
+                               if (tqe_q[ac]->type == WILC_CFG_PKT) {
+                                       vmm_table[i] |= BIT(10);
+                                       PRINT_INFO(vif->ndev, TX_DBG,
+                                                  "VMMTable entry changed for CFG packet = %d\n",
+                                                  vmm_table[i]);
+                               }
+                               cpu_to_le32s(&vmm_table[i]);
+                               vmm_entries_ac[i] = ac;
+
+                               i++;
+                               sum += vmm_sz;
+                               PRINT_INFO(vif->ndev, TX_DBG, "sum = %d\n",
+                                          sum);
+                               tqe_q[ac] = txq_get_next(wilc, tqe_q[ac], ac);
+                       }
+               }
+               num_pkts_to_add = ac_preserve_ratio;
+       } while (!max_size_over && ac_exist);
+
+       if (i == 0) {
+               PRINT_INFO(vif->ndev, TX_DBG, "Nothing in TX-Q\n");
+               goto out;
+       }
+       vmm_table[i] = 0x0;
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+       counter = 0;
+       func = wilc->hif_func;
+       do {
+               ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
+               if (!ret) {
+                       PRINT_ER(vif->ndev, "fail read reg vmm_tbl_entry..\n");
+                       break;
+               }
+               if ((reg & 0x1) == 0) {
+                       ac_pkt_count(reg, ac_fw_count);
+                       ac_acm_bit(wilc, reg);
+                       break;
+               }
+
+               counter++;
+               if (counter > 200) {
+                       counter = 0;
+                       PRINT_INFO(vif->ndev, TX_DBG,
+                                   "Looping in tx ctrl , force quit\n");
+                       ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
+                       break;
+               }
+       } while (!wilc->quit);
+
+       if (!ret)
+               goto out_release_bus;
+
+       timeout = 200;
+       do {
+               ret = func->hif_block_tx(wilc,
+                                        VMM_TBL_RX_SHADOW_BASE,
+                                        (u8 *)vmm_table,
+                                        ((i + 1) * 4));
+               if (!ret) {
+                       PRINT_ER(vif->ndev, "ERR block TX of VMM table.\n");
+                       break;
+               }
+
+               if (wilc->chip == WILC_1000) {
+                       ret = wilc->hif_func->hif_write_reg(wilc,
+                                                           WILC_HOST_VMM_CTL,
+                                                           0x2);
+                       if (!ret) {
+                               PRINT_ER(vif->ndev,
+                                         "fail write reg host_vmm_ctl..\n");
+                               break;
+                       }
+
+                       do {
+                               ret = func->hif_read_reg(wilc,
+                                                     WILC_HOST_VMM_CTL,
+                                                     &reg);
+                               if (!ret)
+                                       break;
+                               if ((reg >> 2) & 0x1) {
+                                       entries = ((reg >> 3) & 0x3f);
+                                       break;
+                               }
+                       } while (--timeout);
+               } else {
+                       ret = func->hif_write_reg(wilc,
+                                             WILC_HOST_VMM_CTL,
+                                             0);
+                       if (!ret) {
+                               PRINT_ER(vif->ndev,
+                                         "fail write reg host_vmm_ctl..\n");
+                               break;
+                       }
+                       /* interrupt firmware */
+                       ret = func->hif_write_reg(wilc,
+                                             WILC_INTERRUPT_CORTUS_0,
+                                             1);
+                       if (!ret) {
+                               PRINT_ER(vif->ndev,
+                                         "fail write reg WILC_INTERRUPT_CORTUS_0..\n");
+                               break;
+                       }
+
+                       do {
+                               ret = func->hif_read_reg(wilc,
+                                                     WILC_INTERRUPT_CORTUS_0,
+                                                     &reg);
+                               if (!ret) {
+                                       PRINT_ER(vif->ndev,
+                                                 "fail read reg WILC_INTERRUPT_CORTUS_0..\n");
+                                       break;
+                               }
+                               if (reg == 0) {
+                                       // Get the entries
+
+                                       ret = func->hif_read_reg(wilc,
+                                                             WILC_HOST_VMM_CTL,
+                                                             &reg);
+                                       if (!ret) {
+                                               PRINT_ER(vif->ndev,
+                                                         "fail read reg host_vmm_ctl..\n");
+                                               break;
+                                       }
+                                       entries = ((reg >> 3) & 0x3f);
+                                       break;
+                               }
+                       } while (--timeout);
+               }
+               if (timeout <= 0) {
+                       ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
+                       break;
+               }
+
+               if (!ret)
+                       break;
+
+               if (entries == 0) {
+                       PRINT_INFO(vif->ndev, TX_DBG,
+                                  "no buffer in the chip (reg: %08x), retry later [[ %d, %x ]]\n",
+                                  reg, i, vmm_table[i-1]);
+                       ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
+                       if (!ret) {
+                               PRINT_ER(vif->ndev,
+                                         "fail read reg WILC_HOST_TX_CTRL..\n");
+                               break;
+                       }
+                       reg &= ~BIT(0);
+                       ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
+                       if (!ret) {
+                               PRINT_ER(vif->ndev,
+                                         "fail write reg WILC_HOST_TX_CTRL..\n");
+                               break;
+                       }
+                       break;
+               }
+               break;
+       } while (1);
+
+       if (!ret)
+               goto out_release_bus;
+
+       if (entries == 0) {
+               ret = -ENOBUFS;
+               goto out_release_bus;
+       }
+
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+       schedule();
+       offset = 0;
+       i = 0;
+       do {
+               struct txq_entry_t *tqe;
+               u32 header, buffer_offset;
+
+               tqe = wilc_wlan_txq_remove_from_head(dev, vmm_entries_ac[i]);
+               ac_pkt_num_to_chip[vmm_entries_ac[i]]++;
+               if (!tqe)
+                       break;
+
+               if (vmm_table[i] == 0)
+                       break;
+
+               le32_to_cpus(&vmm_table[i]);
+               vmm_sz = (vmm_table[i] & 0x3ff);
+               vmm_sz *= 4;
+               header = (tqe->type << 31) |
+                        (tqe->buffer_size << 15) |
+                        vmm_sz;
+               if (tqe->type == WILC_MGMT_PKT)
+                       header |= BIT(30);
+               else
+                       header &= ~BIT(30);
+
+               cpu_to_le32s(&header);
+               memcpy(&txb[offset], &header, 4);
+               if (tqe->type == WILC_CFG_PKT) {
+                       buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
+               } else if (tqe->type == WILC_NET_PKT) {
+                       char *bssid = ((struct tx_complete_data *)
+                                      (tqe->priv))->bssid;
+                       int prio = tqe->q_num;
+
+                       buffer_offset = ETH_ETHERNET_HDR_OFFSET;
+                       memcpy(&txb[offset + 4], &prio, sizeof(prio));
+                       memcpy(&txb[offset + 8], bssid, 6);
+               } else {
+                       buffer_offset = HOST_HDR_OFFSET;
+               }
+
+               memcpy(&txb[offset + buffer_offset],
+                      tqe->buffer, tqe->buffer_size);
+               offset += vmm_sz;
+               i++;
+               tqe->status = 1;
+               if (tqe->tx_complete_func)
+                       tqe->tx_complete_func(tqe->priv,
+                                             tqe->status);
+               if (tqe->ack_idx != NOT_TCP_ACK &&
+                   tqe->ack_idx < MAX_PENDING_ACKS)
+                       vif->ack_filter.pending_acks[tqe->ack_idx].txqe = NULL;
+               kfree(tqe);
+       } while (--entries);
+       for (i = 0; i < NQUEUES; i++)
+               ac_fw_count[i] += ac_pkt_num_to_chip[i];
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+       ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "fail start tx VMM ...\n");
+               goto out_release_bus;
+       }
+
+       ret = func->hif_block_tx_ext(wilc, 0, txb, offset);
+       if (!ret)
+               PRINT_ER(vif->ndev, "fail block tx ext...\n");
+
+out_release_bus:
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+       schedule();
+
+out:
+       mutex_unlock(&wilc->txq_add_to_head_cs);
+
+       PRINT_INFO(vif->ndev, TX_DBG, "THREAD: Exiting txq\n");
+       *txq_count = wilc->txq_entries;
+       if (ret == 1)
+               cfg_packet_timeout = 0;
+       return ret;
+}
+
+static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size)
+{
+       int offset = 0;
+       u32 header;
+       u32 pkt_len, pkt_offset, tp_len;
+       int is_cfg_packet;
+       u8 *buff_ptr;
+       struct wilc_vif *vif = wilc->vif[0];
+
+       do {
+               PRINT_INFO(vif->ndev, RX_DBG, "Handling rx buffer\n");
+               buff_ptr = buffer + offset;
+               memcpy(&header, buff_ptr, 4);
+               le32_to_cpus(&header);
+               PRINT_INFO(vif->ndev, RX_DBG,
+                          "Header = %04x - Offset = %d\n", header, offset);
+
+               is_cfg_packet = (header >> 31) & 0x1;
+               pkt_offset = (header >> 22) & 0x1ff;
+               tp_len = (header >> 11) & 0x7ff;
+               pkt_len = header & 0x7ff;
+
+               if (pkt_len == 0 || tp_len == 0) {
+                       PRINT_INFO(vif->ndev, RX_DBG,
+                                  "Data corrupted %d, %d\n",
+                                  pkt_len, tp_len);
+                       break;
+               }
+
+               if (is_cfg_packet) {
+                       struct wilc_cfg_rsp rsp;
+
+                       buff_ptr += pkt_offset;
+
+                       cfg_indicate_rx(wilc, buff_ptr, pkt_len,
+                                       &rsp);
+                       if (rsp.type == WILC_CFG_RSP) {
+                               PRINT_INFO(vif->ndev, RX_DBG,
+                                       "cfg_seq %d rsp.seq %d\n",
+                                       wilc->cfg_seq_no, rsp.seq_no);
+
+                               if (wilc->cfg_seq_no == rsp.seq_no)
+                                       complete(&wilc->cfg_event);
+                       } else if (rsp.type == WILC_CFG_RSP_STATUS) {
+                               wilc_mac_indicate(wilc);
+                       }
+               } else if (pkt_offset & IS_MANAGMEMENT) {
+                       buff_ptr += HOST_HDR_OFFSET;
+                       wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len);
+               } else if (pkt_offset & IS_MON_PKT) {
+                       /* packet received on monitor interface */
+                       buff_ptr += HOST_HDR_OFFSET;
+                       wilc_wfi_handle_monitor_rx(wilc, buff_ptr, pkt_len);
+               } else if (pkt_len > 0) {
+                       struct net_device *wilc_netdev;
+
+                       wilc_netdev = get_if_handler(wilc, buff_ptr);
+                       if (!wilc_netdev) {
+                               PRINT_ER(vif->ndev,
+                                        "wilc_netdev in wilc is NULL");
+                               return;
+                       }
+                       vif = netdev_priv(wilc_netdev);
+                       wilc_frmw_to_host(vif, buff_ptr, pkt_len,
+                                         pkt_offset, PKT_STATUS_NEW);
+               }
+
+               offset += tp_len;
+               if (offset >= size)
+                       break;
+       } while (1);
+}
+
+static void wilc_wlan_handle_rxq(struct wilc *wilc)
+{
+       int size;
+       u8 *buffer;
+       struct rxq_entry_t *rqe;
+       struct wilc_vif *vif = wilc->vif[0];
+
+       do {
+               if (wilc->quit) {
+                       PRINT_INFO(vif->ndev, RX_DBG,
+                                  "Quitting. Exit handle RX queue\n");
+                       complete(&wilc->cfg_event);
+                       break;
+               }
+               rqe = rxq_remove(wilc);
+               if (!rqe) {
+                       PRINT_INFO(vif->ndev, RX_DBG,
+                                  "nothing in RX queue\n");
+                       break;
+               }
+
+               buffer = rqe->buffer;
+               size = rqe->buffer_size;
+               PRINT_INFO(vif->ndev, RX_DBG,
+                          "rxQ entery Size = %d - Address = %p\n",
+                          size, buffer);
+
+               wilc_wlan_handle_rx_buff(wilc, buffer, size);
+
+               kfree(rqe);
+       } while (1);
+
+       PRINT_INFO(vif->ndev, RX_DBG, "THREAD: Exiting RX thread\n");
+}
+
+static void wilc_unknown_isr_ext(struct wilc *wilc)
+{
+       wilc->hif_func->hif_clear_int_ext(wilc, 0);
+}
+
+static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status)
+{
+       u32 offset = wilc->rx_buffer_offset;
+       u8 *buffer = NULL;
+       u32 size;
+       u32 retries = 0;
+       int ret = 0;
+       struct rxq_entry_t *rqe;
+       struct wilc_vif *vif = wilc->vif[0];
+
+       size = (int_status & 0x7fff) << 2;
+
+       while (!size && retries < 10) {
+               PRINT_ER(vif->ndev,
+                        "RX Size equal zero Trying to read it again\n");
+               wilc->hif_func->hif_read_size(wilc, &size);
+               size = (size & 0x7fff) << 2;
+               retries++;
+       }
+
+       if (size <= 0)
+               return;
+
+       if (WILC_RX_BUFF_SIZE - offset < size)
+               offset = 0;
+
+       buffer = &wilc->rx_buffer[offset];
+
+       wilc->hif_func->hif_clear_int_ext(wilc, DATA_INT_CLR | ENABLE_RX_VMM);
+
+       ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "fail block rx\n");
+               return;
+       }
+
+       offset += size;
+       wilc->rx_buffer_offset = offset;
+       rqe = kmalloc(sizeof(*rqe), GFP_KERNEL);
+       if (!rqe)
+               return;
+
+       rqe->buffer = buffer;
+       rqe->buffer_size = size;
+       PRINT_INFO(vif->ndev, RX_DBG,
+                  "rxq entery Size= %d Address= %p\n",
+                  rqe->buffer_size, rqe->buffer);
+       rxq_add(wilc, rqe);
+       wilc_wlan_handle_rxq(wilc);
+}
+
+void wilc_handle_isr(struct wilc *wilc)
+{
+       u32 int_status;
+       struct wilc_vif *vif = wilc->vif[0];
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+       wilc->hif_func->hif_read_int(wilc, &int_status);
+
+       if (int_status & DATA_INT_EXT)
+               wilc_wlan_handle_isr_ext(wilc, int_status);
+
+       if (!(int_status & (ALL_INT_EXT))) {
+               PRINT_WRN(vif->ndev, TX_DBG, ">> UNKNOWN_INTERRUPT - 0x%08x\n",
+                         int_status);
+               wilc_unknown_isr_ext(wilc);
+       }
+
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+}
+
+int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
+                               u32 buffer_size)
+{
+       u32 offset;
+       u32 addr, size, size2, blksz;
+       u8 *dma_buffer;
+       int ret = 0;
+       u32 reg = 0;
+       struct wilc_vif *vif = wilc->vif[0];
+
+       blksz = BIT(12);
+
+       dma_buffer = kmalloc(blksz, GFP_KERNEL);
+       if (!dma_buffer) {
+               PRINT_ER(vif->ndev,
+                        "Can't allocate buffer for fw download IO error\n");
+               return -EIO;
+       }
+
+       offset = 0;
+       PRINT_INFO(vif->ndev, INIT_DBG, "Downloading firmware size = %d\n",
+                  buffer_size);
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+       wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+       reg &= ~(1ul << 10);
+       ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+       wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+       if ((reg & (1ul << 10)) != 0)
+               PRINT_ER(vif->ndev, "Failed to reset Wifi CPU\n");
+
+       release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI);
+       do {
+               memcpy(&addr, &buffer[offset], 4);
+               memcpy(&size, &buffer[offset + 4], 4);
+               le32_to_cpus(&addr);
+               le32_to_cpus(&size);
+               acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+               offset += 8;
+               while (((int)size) && (offset < buffer_size)) {
+                       if (size <= blksz)
+                               size2 = size;
+                       else
+                               size2 = blksz;
+
+                       memcpy(dma_buffer, &buffer[offset], size2);
+                       ret = wilc->hif_func->hif_block_tx(wilc, addr,
+                                                          dma_buffer, size2);
+                       if (!ret)
+                               break;
+
+                       addr += size2;
+                       offset += size2;
+                       size -= size2;
+               }
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+               if (!ret) {
+                       ret = -EIO;
+                       PRINT_ER(vif->ndev, "Bus error\n");
+                       goto fail;
+               }
+               PRINT_INFO(vif->ndev, INIT_DBG, "Offset = %d\n", offset);
+       } while (offset < buffer_size);
+
+fail:
+
+       kfree(dma_buffer);
+
+       return (ret < 0) ? ret : 0;
+}
+
+int wilc_wlan_start(struct wilc *wilc)
+{
+       u32 reg = 0;
+       int ret;
+       struct wilc_vif *vif = wilc->vif[0];
+
+       if (wilc->io_type == WILC_HIF_SDIO ||
+           wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ)
+               reg |= BIT(3);
+       else if (wilc->io_type == WILC_HIF_SPI)
+               reg = 1;
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+       ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev,
+                        "[wilc start]: fail write reg vmm_core_cfg...\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return -EIO;
+       }
+       reg = 0;
+       if (wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ)
+               reg |= WILC_HAVE_SDIO_IRQ_GPIO;
+
+       if (wilc->chip == WILC_3000)
+               reg |= WILC_HAVE_SLEEP_CLK_SRC_RTC;
+
+       ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev,
+                        "[wilc start]: fail write WILC_GP_REG_1...\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return -EIO;
+       }
+
+       wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT);
+
+
+       wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+       if ((reg & BIT(10)) == BIT(10)) {
+               reg &= ~BIT(10);
+               wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+               wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+       }
+
+       reg |= BIT(10);
+       ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+       wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+
+       if (ret >= 0)
+               wilc->initialized = 1;
+       else
+               wilc->initialized = 0;
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+       return (ret < 0) ? ret : 0;
+}
+
+int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif)
+{
+       u32 reg = 0;
+       int ret;
+       u8 timeout = 10;
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+       /* Clear Wifi mode*/
+       ret = wilc->hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL, &reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "Error while reading reg\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return ret;
+       }
+
+       reg &= ~BIT(0);
+       ret = wilc->hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL, reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "Error while writing reg\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return ret;
+       }
+
+       /* Configure the power sequencer to ignore WIFI sleep signal on making
+        * chip sleep decision
+        */
+       ret = wilc->hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, &reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "Error while reading reg\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return ret;
+       }
+
+       reg &= ~BIT(28);
+       ret = wilc->hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "Error while writing reg\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return ret;
+       }
+
+       ret = wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "Error while reading reg\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return ret;
+       }
+
+       reg &= ~BIT(10);
+       ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "Error while writing reg\n");
+               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+               return ret;
+       }
+
+       do {
+               ret = wilc->hif_func->hif_read_reg(wilc,
+                                                  WILC_GLB_RESET_0, &reg);
+               if (!ret) {
+                       PRINT_ER(vif->ndev, "Error while reading reg\n");
+                       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP,
+                                   DEV_WIFI);
+                       return ret;
+               }
+               PRINT_INFO(vif->ndev, GENERIC_DBG,
+                          "Read RESET Reg %x : Retry%d\n", reg, timeout);
+               if ((reg & BIT(10))) {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Bit 10 not reset : Retry %d\n", timeout);
+                       reg &= ~BIT(10);
+                       ret = wilc->hif_func->hif_write_reg(wilc,
+                                                           WILC_GLB_RESET_0,
+                                                           reg);
+                       timeout--;
+               } else {
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Bit 10 reset after : Retry %d\n", timeout);
+                       ret = wilc->hif_func->hif_read_reg(wilc,
+                                                          WILC_GLB_RESET_0,
+                                                          &reg);
+                       if (!ret) {
+                               PRINT_ER(vif->ndev, "Error reading reg\n");
+                               release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP,
+                                           DEV_WIFI);
+                               return ret;
+                       }
+                       PRINT_INFO(vif->ndev, GENERIC_DBG,
+                                  "Read RESET Reg %x : Retry%d\n", reg,
+                                  timeout);
+                       break;
+               }
+
+       } while (timeout);
+
+       if (wilc->chip == WILC_1000) {
+               reg = (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(8) | BIT(9) |
+                      BIT(26) | BIT(29) | BIT(30) | BIT(31));
+       } else {
+               reg = (BIT(0) | BIT(2) | BIT(3) | BIT(8) | BIT(9) |
+                      BIT(20) | BIT(26) | BIT(29) | BIT(30) | BIT(31));
+       }
+
+       wilc->hif_func->hif_read_reg(wilc, WILC_FW_HOST_COMM, &reg);
+       reg = BIT(0);
+
+       ret = wilc->hif_func->hif_write_reg(wilc, WILC_FW_HOST_COMM, reg);
+
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+       return ret;
+}
+
+void wilc_wlan_cleanup(struct net_device *dev)
+{
+       struct txq_entry_t *tqe;
+       struct rxq_entry_t *rqe;
+       u8 ac;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+
+       wilc->quit = 1;
+       for (ac = 0; ac < NQUEUES; ac++) {
+               do {
+                       tqe = wilc_wlan_txq_remove_from_head(dev, ac);
+                       if (!tqe)
+                               break;
+                       if (tqe->tx_complete_func)
+                               tqe->tx_complete_func(tqe->priv, 0);
+                       kfree(tqe);
+               } while (1);
+       }
+
+       do {
+               rqe = rxq_remove(wilc);
+               if (!rqe)
+                       break;
+               kfree(rqe);
+       } while (1);
+
+       kfree(wilc->rx_buffer);
+       wilc->rx_buffer = NULL;
+       kfree(wilc->tx_buffer);
+       wilc->tx_buffer = NULL;
+}
+
+static int wilc_wlan_cfg_commit(struct wilc_vif *vif, int type,
+                               u32 drv_handler)
+{
+       struct wilc *wilc = vif->wilc;
+       struct wilc_cfg_frame *cfg = &wilc->cfg_frame;
+       int t_len = wilc->cfg_frame_offset + sizeof(struct wilc_cfg_cmd_hdr);
+
+       if (type == WILC_CFG_SET)
+               cfg->hdr.cmd_type = 'W';
+       else
+               cfg->hdr.cmd_type = 'Q';
+
+       cfg->hdr.seq_no = wilc->cfg_seq_no % 256;
+       cfg->hdr.total_len = cpu_to_le16(t_len);
+       cfg->hdr.driver_handler = cpu_to_le32(drv_handler);
+       wilc->cfg_seq_no = cfg->hdr.seq_no;
+
+       if (!wilc_wlan_txq_add_cfg_pkt(vif, (u8 *)&cfg->hdr, t_len))
+               return -1;
+
+       return 0;
+}
+
+int cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
+                     u32 buffer_size, int commit, u32 drv_handler)
+{
+       u32 offset;
+       int ret_size;
+       struct wilc *wilc = vif->wilc;
+
+       mutex_lock(&wilc->cfg_cmd_lock);
+
+       if (start)
+               wilc->cfg_frame_offset = 0;
+
+       offset = wilc->cfg_frame_offset;
+       ret_size = cfg_set_wid(vif, wilc->cfg_frame.frame, offset,
+                                        wid, buffer, buffer_size);
+       offset += ret_size;
+       wilc->cfg_frame_offset = offset;
+
+       if (!commit) {
+               mutex_unlock(&wilc->cfg_cmd_lock);
+               return ret_size;
+       }
+
+       PRINT_INFO(vif->ndev, TX_DBG,
+                  "[WILC]PACKET Commit with sequence number%d\n",
+                  wilc->cfg_seq_no);
+
+       if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler))
+               ret_size = 0;
+
+       if (!wait_for_completion_timeout(&wilc->cfg_event,
+                                        WILC_CFG_PKTS_TIMEOUT)) {
+               PRINT_ER(vif->ndev, "Timed Out\n");
+               ret_size = 0;
+       }
+
+       wilc->cfg_frame_offset = 0;
+       wilc->cfg_seq_no += 1;
+       mutex_unlock(&wilc->cfg_cmd_lock);
+
+       return ret_size;
+}
+
+int cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
+                     u32 drv_handler)
+{
+       u32 offset;
+       int ret_size;
+       struct wilc *wilc = vif->wilc;
+
+       mutex_lock(&wilc->cfg_cmd_lock);
+
+       if (start)
+               wilc->cfg_frame_offset = 0;
+
+       offset = wilc->cfg_frame_offset;
+       ret_size = cfg_get_wid(wilc->cfg_frame.frame, offset, wid);
+       offset += ret_size;
+       wilc->cfg_frame_offset = offset;
+
+       if (!commit) {
+               mutex_unlock(&wilc->cfg_cmd_lock);
+               return ret_size;
+       }
+
+       if (wilc_wlan_cfg_commit(vif, WILC_CFG_QUERY, drv_handler))
+               ret_size = 0;
+
+       if (!wait_for_completion_timeout(&wilc->cfg_event,
+                                        WILC_CFG_PKTS_TIMEOUT)) {
+               PRINT_INFO(vif->ndev, TX_DBG, "Timed Out\n");
+               ret_size = 0;
+       }
+       PRINT_INFO(vif->ndev, TX_DBG, "Get Response received\n");
+       wilc->cfg_frame_offset = 0;
+       wilc->cfg_seq_no += 1;
+       mutex_unlock(&wilc->cfg_cmd_lock);
+
+       return ret_size;
+}
+
+int cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size)
+{
+       return cfg_get_wid_value(wl, wid, buffer, buffer_size);
+}
+unsigned int cfg_packet_timeout;
+int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
+                        u32 count, u32 drv)
+{
+       int i;
+       int ret = 0;
+
+       if (wait_for_recovery) {
+               PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+                          "Host interface is suspended\n");
+               while (wait_for_recovery)
+                       msleep(300);
+               PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+                          "Host interface is resumed\n");
+       }
+
+       if (mode == WILC_GET_CFG) {
+               for (i = 0; i < count; i++) {
+                       PRINT_D(vif->ndev, CORECONFIG_DBG,
+                               "Sending CFG packet [%d][%d]\n", !i,
+                               (i == count - 1));
+                       if (!cfg_get(vif, !i, wids[i].id, (i == count - 1),
+                                    drv)) {
+                               ret = -ETIMEDOUT;
+                               PRINT_ER(vif->ndev, "Get Timed out\n");
+                               break;
+                       }
+               }
+               for (i = 0; i < count; i++) {
+                       wids[i].size = cfg_get_val(vif->wilc, wids[i].id,
+                                                            wids[i].val,
+                                                            wids[i].size);
+               }
+       } else if (mode == WILC_SET_CFG) {
+               for (i = 0; i < count; i++) {
+                       PRINT_INFO(vif->ndev, CORECONFIG_DBG,
+                                  "Sending config SET PACKET WID:%x\n",
+                                  wids[i].id);
+                       if (!cfg_set(vif, !i, wids[i].id, wids[i].val,
+                                    wids[i].size, (i == count - 1), drv)) {
+                               ret = -ETIMEDOUT;
+                               PRINT_ER(vif->ndev, "Set Timed out\n");
+                               break;
+                       }
+               }
+       }
+       cfg_packet_timeout = (ret < 0) ? cfg_packet_timeout + 1 : 0;
+       return ret;
+}
+
+static u32 init_chip(struct net_device *dev)
+{
+       u32 chipid;
+       u32 reg, ret = 0;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc = vif->wilc;
+
+       acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI);
+
+       chipid = wilc_get_chipid(wilc, true);
+
+       ret = wilc->hif_func->hif_read_reg(wilc, 0x1118, &reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "fail read reg 0x1118\n");
+               goto end;
+       }
+
+       reg |= BIT(0);
+       ret = wilc->hif_func->hif_write_reg(wilc, 0x1118, reg);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "fail write reg 0x1118\n");
+               goto end;
+       }
+       ret = wilc->hif_func->hif_write_reg(wilc, 0xc0000, 0x71);
+       if (!ret) {
+               PRINT_ER(vif->ndev, "fail write reg 0xc0000 ...\n");
+               goto end;
+       }
+
+       if (wilc->chip == WILC_3000) {
+               ret = wilc->hif_func->hif_read_reg(wilc, 0x207ac, &reg);
+               PRINT_INFO(vif->ndev, INIT_DBG, "Bootrom sts = %x\n", reg);
+               ret = wilc->hif_func->hif_write_reg(wilc, 0x4f0000,
+                                                   0x71);
+               if (!ret) {
+                       PRINT_ER(vif->ndev, "fail write reg 0x4f0000 ...\n");
+                       goto end;
+               }
+       }
+
+end:
+       release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI);
+
+       return ret;
+}
+
+u32 wilc_get_chipid(struct wilc *wilc, bool update)
+{
+       static u32 chipid;
+       int ret;
+       u32 tempchipid = 0;
+
+       if (chipid == 0 || update) {
+               ret = wilc->hif_func->hif_read_reg(wilc, 0x3b0000,
+                                                    &tempchipid);
+               if (!ret)
+                       pr_err("[wilc start]: fail read reg 0x3b0000\n");
+               if (!is_wilc3000(tempchipid)) {
+                       wilc->hif_func->hif_read_reg(wilc, 0x1000,
+                                                    &tempchipid);
+                       if (!is_wilc1000(tempchipid)) {
+                               chipid = 0;
+                               return chipid;
+                       }
+                       if (tempchipid < 0x1003a0) {
+                               pr_err("WILC1002 isn't suported %x\n", chipid);
+                               chipid = 0;
+                               return chipid;
+                       }
+               }
+               chipid = tempchipid;
+       }
+
+       return chipid;
+}
+
+int wilc_wlan_init(struct net_device *dev)
+{
+       int ret = 0;
+       struct wilc_vif *vif = netdev_priv(dev);
+       struct wilc *wilc;
+
+       wilc = vif->wilc;
+
+       wilc->quit = 0;
+
+       PRINT_INFO(vif->ndev, INIT_DBG, "Initializing WILC_Wlan\n");
+
+       if (!wilc->hif_func->hif_is_init(wilc)) {
+               acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, DEV_WIFI);
+               if (!wilc->hif_func->hif_init(wilc, false)) {
+                       ret = -EIO;
+                       release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI);
+                       goto fail;
+               }
+               release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI);
+       }
+
+       if (!wilc->tx_buffer)
+               wilc->tx_buffer = kmalloc(WILC_TX_BUFF_SIZE, GFP_KERNEL);
+
+       if (!wilc->tx_buffer) {
+               ret = -ENOBUFS;
+               PRINT_ER(vif->ndev, "Can't allocate Tx Buffer");
+               goto fail;
+       }
+
+       if (!wilc->rx_buffer)
+               wilc->rx_buffer = kmalloc(WILC_RX_BUFF_SIZE, GFP_KERNEL);
+       PRINT_D(vif->ndev, TX_DBG, "g_wlan.rx_buffer =%p\n", wilc->rx_buffer);
+       if (!wilc->rx_buffer) {
+               ret = -ENOBUFS;
+               PRINT_ER(vif->ndev, "Can't allocate Rx Buffer");
+               goto fail;
+       }
+
+       if (!init_chip(dev)) {
+               ret = -EIO;
+               goto fail;
+       }
+
+       return 1;
+
+fail:
+
+       kfree(wilc->rx_buffer);
+       wilc->rx_buffer = NULL;
+       kfree(wilc->tx_buffer);
+       wilc->tx_buffer = NULL;
+
+       return ret;
+}
diff --git a/drivers/net/wireless/mchp/wilc_wlan.h b/drivers/net/wireless/mchp/wilc_wlan.h
new file mode 100644 (file)
index 0000000..ad230ce
--- /dev/null
@@ -0,0 +1,369 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WLAN_H
+#define WILC_WLAN_H
+
+#include <linux/types.h>
+#include <linux/version.h>
+
+static inline bool is_wilc1000(u32 id)
+{
+       return (id & 0xfffff000) == 0x100000;
+}
+
+static inline bool is_wilc3000(u32 id)
+{
+       return (id & 0xfffff000) == 0x300000;
+}
+
+/********************************************
+ *
+ *      Mac eth header length
+ *
+ ********************************************/
+#define MAX_MAC_HDR_LEN                        26 /* QOS_MAC_HDR_LEN */
+#define SUB_MSDU_HEADER_LENGTH         14
+#define SNAP_HDR_LEN                   8
+#define ETHERNET_HDR_LEN               14
+#define WORD_ALIGNMENT_PAD             0
+
+#define ETH_ETHERNET_HDR_OFFSET                (MAX_MAC_HDR_LEN + \
+                                        SUB_MSDU_HEADER_LENGTH + \
+                                        SNAP_HDR_LEN - \
+                                        ETHERNET_HDR_LEN + \
+                                        WORD_ALIGNMENT_PAD)
+
+#define HOST_HDR_OFFSET                        4
+#define ETHERNET_HDR_LEN               14
+#define IP_HDR_LEN                     20
+#define IP_HDR_OFFSET                  ETHERNET_HDR_LEN
+#define UDP_HDR_OFFSET                 (IP_HDR_LEN + IP_HDR_OFFSET)
+#define UDP_HDR_LEN                    8
+#define UDP_DATA_OFFSET                        (UDP_HDR_OFFSET + UDP_HDR_LEN)
+#define ETH_CONFIG_PKT_HDR_LEN         UDP_DATA_OFFSET
+
+#define ETH_CONFIG_PKT_HDR_OFFSET      (ETH_ETHERNET_HDR_OFFSET + \
+                                        ETH_CONFIG_PKT_HDR_LEN)
+#define PKT_STATUS_NEW          0
+#define PKT_STATUS_BUFFERED     1
+
+/********************************************
+ *
+ *      Register Defines
+ *
+ ********************************************/
+#define WILC_PERIPH_REG_BASE           0x1000
+#define WILC_CHIPID                    WILC_PERIPH_REG_BASE
+#define WILC_GLB_RESET_0               (WILC_PERIPH_REG_BASE + 0x400)
+#define WILC_PIN_MUX_0                 (WILC_PERIPH_REG_BASE + 0x408)
+#define WILC_HOST_TX_CTRL              (WILC_PERIPH_REG_BASE + 0x6c)
+#define WILC_HOST_RX_CTRL_0            (WILC_PERIPH_REG_BASE + 0x70)
+#define WILC_HOST_RX_CTRL_1            (WILC_PERIPH_REG_BASE + 0x74)
+#define WILC_HOST_VMM_CTL              (WILC_PERIPH_REG_BASE + 0x78)
+#define WILC_HOST_RX_CTRL              (WILC_PERIPH_REG_BASE + 0x80)
+#define WILC_HOST_RX_EXTRA_SIZE                (WILC_PERIPH_REG_BASE + 0x84)
+#define WILC_HOST_TX_CTRL_1            (WILC_PERIPH_REG_BASE + 0x88)
+#define WILC_INTERRUPT_CORTUS_0                (WILC_PERIPH_REG_BASE + 0xa8)
+#define WILC_MISC                      (WILC_PERIPH_REG_BASE + 0x428)
+#define WILC_INTR_REG_BASE             (WILC_PERIPH_REG_BASE + 0xa00)
+#define WILC_INTR_ENABLE               WILC_INTR_REG_BASE
+#define WILC_INTR2_ENABLE              (WILC_INTR_REG_BASE + 4)
+
+#define WILC_INTR_POLARITY             (WILC_INTR_REG_BASE + 0x10)
+#define WILC_INTR_TYPE                 (WILC_INTR_REG_BASE + 0x20)
+#define WILC_INTR_CLEAR                        (WILC_INTR_REG_BASE + 0x30)
+#define WILC_INTR_STATUS               (WILC_INTR_REG_BASE + 0x40)
+
+#define WILC_VMM_TBL_SIZE              64
+#define WILC_VMM_TX_TBL_BASE           0x150400
+#define WILC_VMM_RX_TBL_BASE           0x150500
+
+#define WILC_VMM_BASE                  0x150000
+#define WILC_VMM_CORE_CTL              WILC_VMM_BASE
+#define WILC_VMM_TBL_CTL               (WILC_VMM_BASE + 0x4)
+#define WILC_VMM_TBL_ENTRY             (WILC_VMM_BASE + 0x8)
+#define WILC_VMM_TBL0_SIZE             (WILC_VMM_BASE + 0xc)
+#define WILC_VMM_TO_HOST_SIZE          (WILC_VMM_BASE + 0x10)
+#define WILC_VMM_CORE_CFG              (WILC_VMM_BASE + 0x14)
+#define WILC_VMM_TBL_ACTIVE            (WILC_VMM_BASE + 040)
+#define WILC_VMM_TBL_STATUS            (WILC_VMM_BASE + 0x44)
+
+#define WILC_SPI_REG_BASE              0xe800
+#define WILC_SPI_CTL                   WILC_SPI_REG_BASE
+#define WILC_SPI_MASTER_DMA_ADDR       (WILC_SPI_REG_BASE + 0x4)
+#define WILC_SPI_MASTER_DMA_COUNT      (WILC_SPI_REG_BASE + 0x8)
+#define WILC_SPI_SLAVE_DMA_ADDR                (WILC_SPI_REG_BASE + 0xc)
+#define WILC_SPI_SLAVE_DMA_COUNT       (WILC_SPI_REG_BASE + 0x10)
+#define WILC_SPI_TX_MODE               (WILC_SPI_REG_BASE + 0x20)
+#define WILC_SPI_PROTOCOL_CONFIG       (WILC_SPI_REG_BASE + 0x24)
+#define WILC_SPI_INTR_CTL              (WILC_SPI_REG_BASE + 0x2c)
+
+#define WILC_SPI_PROTOCOL_OFFSET       (WILC_SPI_PROTOCOL_CONFIG - \
+                                        WILC_SPI_REG_BASE)
+
+#define WILC_AHB_DATA_MEM_BASE         0x30000
+#define WILC_AHB_SHARE_MEM_BASE                0xd0000
+
+#define VMM_TBL_RX_SHADOW_BASE         WILC_AHB_SHARE_MEM_BASE
+#define VMM_TBL_RX_SHADOW_SIZE         256
+
+#define WILC_FW_HOST_COMM              0x13c0
+#define WILC_GP_REG_0                  0x149c
+#define WILC_GP_REG_1                  0x14a0
+
+#define WILC_COEXIST_CTL               0x161E00
+#define GLOBAL_MODE_CONTROL            0x1614
+#define PWR_SEQ_MISC_CTRL              0x3008
+#define COE_AUTO_PS_ON_NULL_PKT                0x160468
+#define COE_AUTO_PS_OFF_NULL_PKT       0x16046C
+#define CCA_CTL_2 (0x160EF4)
+#define CCA_CTL_7 (0x160F08)
+
+#define WILC_HAVE_SDIO_IRQ_GPIO                BIT(0)
+#define WILC_HAVE_SLEEP_CLK_SRC_RTC    BIT(2)
+#define WILC_HAVE_SLEEP_CLK_SRC_XO     BIT(3)
+
+/********************************************
+ *
+ *      Wlan Defines
+ *
+ ********************************************/
+#define WILC_CFG_PKT           1
+#define WILC_NET_PKT           0
+#define WILC_MGMT_PKT          2
+
+#define WILC_CFG_SET           1
+#define WILC_CFG_QUERY         0
+
+#define WILC_CFG_RSP           1
+#define WILC_CFG_RSP_STATUS    2
+#define WILC_CFG_RSP_SCAN      3
+#define ABORT_INT              BIT(31)
+
+#define WILC_RX_BUFF_SIZE      (96 * 1024)
+#define WILC_TX_BUFF_SIZE      (64 * 1024)
+
+#define MODALIAS               "WILC_SPI"
+#define GPIO_NUM               0x5B
+#define GPIO_NUM_CHIP_EN       94
+#define GPIO_NUM_RESET         60
+
+#define NQUEUES                        4
+#define VO_AC_COUNT_POS                25
+#define VO_AC_ACM_STAT_POS     24
+#define VI_AC_COUNT_POS                17
+#define VI_AC_ACM_STAT_POS     16
+#define BE_AC_COUNT_POS                9
+#define BE_AC_ACM_STAT_POS     8
+#define BK_AC_COUNT_POS                2
+#define BK_AC_ACM_STAT_POS     1
+#define AC_BUFFER_SIZE         1000
+/*******************************************/
+/*        E0 and later Interrupt flags.    */
+/*******************************************/
+/*******************************************/
+/*        E0 and later Interrupt flags.    */
+/*           IRQ Status word               */
+/* 15:0 = DMA count in words.              */
+/* 16: INT0 flag                           */
+/* 17: INT1 flag                           */
+/* 18: INT2 flag                           */
+/* 19: INT3 flag                           */
+/* 20: INT4 flag                           */
+/* 21: INT5 flag                           */
+/*******************************************/
+#define IRG_FLAGS_OFFSET       16
+#define IRQ_DMA_WD_CNT_MASK    ((1ul << IRG_FLAGS_OFFSET) - 1)
+#define INT_0                  BIT(IRG_FLAGS_OFFSET)
+#define INT_1                  BIT(IRG_FLAGS_OFFSET + 1)
+#define INT_2                  BIT(IRG_FLAGS_OFFSET + 2)
+#define INT_3                  BIT(IRG_FLAGS_OFFSET + 3)
+#define INT_4                  BIT(IRG_FLAGS_OFFSET + 4)
+#define MAX_NUM_INT            5
+
+/*******************************************/
+/*        E0 and later Interrupt flags.    */
+/*           IRQ Clear word                */
+/* 0: Clear INT0                           */
+/* 1: Clear INT1                           */
+/* 2: Clear INT2                           */
+/* 3: Clear INT3                           */
+/* 4: Clear INT4                           */
+/* 5: Clear INT5                           */
+/* 6: Select VMM table 1                   */
+/* 7: Select VMM table 2                   */
+/* 8: Enable VMM                           */
+/*******************************************/
+#define CLR_INT0               BIT(0)
+#define CLR_INT1               BIT(1)
+#define CLR_INT2               BIT(2)
+#define CLR_INT3               BIT(3)
+#define CLR_INT4               BIT(4)
+#define CLR_INT5               BIT(5)
+#define SEL_VMM_TBL0           BIT(6)
+#define SEL_VMM_TBL1           BIT(7)
+#define EN_VMM                 BIT(8)
+
+#define DATA_INT_EXT           INT_0
+#define ALL_INT_EXT            (DATA_INT_EXT)
+#define NUM_INT_EXT            1
+
+#define DATA_INT_CLR           CLR_INT0
+
+#define ENABLE_RX_VMM          (SEL_VMM_TBL1 | EN_VMM)
+#define ENABLE_TX_VMM          (SEL_VMM_TBL0 | EN_VMM)
+/*time for expiring the completion of cfg packets*/
+#define WILC_CFG_PKTS_TIMEOUT  msecs_to_jiffies(3000)
+
+#define IS_MANAGMEMENT         0x100
+#define IS_MANAGMEMENT_CALLBACK        0x080
+#define IS_MGMT_STATUS_SUCCES  0x040
+#define IS_MON_PKT             0x020
+
+/********************************************
+ *
+ *      Tx/Rx Queue Structure
+ *
+ ********************************************/
+
+enum ip_pkt_priority {
+       AC_VO_Q = 0,
+       AC_VI_Q = 1,
+       AC_BE_Q = 2,
+       AC_BK_Q = 3
+};
+
+struct txq_entry_t {
+       struct list_head list;
+       int type;
+       u8 q_num;
+       int ack_idx;
+       u8 *buffer;
+       int buffer_size;
+       void *priv;
+       int status;
+       void (*tx_complete_func)(void *priv, int status);
+};
+
+struct txq_handle {
+       struct txq_entry_t txq_head;
+       u16 count;
+       u8 acm;
+};
+
+struct rxq_entry_t {
+       struct list_head list;
+       u8 *buffer;
+       int buffer_size;
+};
+
+enum wilc_chip_type {
+       WILC_1000,
+       WILC_3000,
+};
+
+/********************************************
+ *
+ *      Host IF Structure
+ *
+ ********************************************/
+struct wilc;
+struct wilc_hif_func {
+       int (*hif_init)(struct wilc *wilc, bool resume);
+       int (*hif_deinit)(struct wilc *wilc);
+       int (*hif_read_reg)(struct wilc *wilc, u32 addr, u32 *data);
+       int (*hif_write_reg)(struct wilc *wilc, u32 addr, u32 data);
+       int (*hif_block_rx)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+       int (*hif_block_tx)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+       int (*hif_read_int)(struct wilc *wilc, u32 *int_status);
+       int (*hif_clear_int_ext)(struct wilc *wilc, u32 val);
+       int (*hif_read_size)(struct wilc *wilc, u32 *size);
+       int (*hif_block_tx_ext)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+       int (*hif_block_rx_ext)(struct wilc *wilc, u32 addr, u8 *buf, u32 size);
+       int (*hif_sync_ext)(struct wilc *wilc, int nint);
+       int (*enable_interrupt)(struct wilc *nic);
+       void (*disable_interrupt)(struct wilc *nic);
+       int (*hif_reset)(struct wilc *wilc);
+       bool (*hif_is_init)(struct wilc *wilc);
+};
+
+#define WILC_MAX_CFG_FRAME_SIZE                1468
+
+struct tx_complete_data {
+       int size;
+       void *buff;
+       u8 *bssid;
+       struct sk_buff *skb;
+       struct wilc_vif *vif;
+};
+
+struct wilc_cfg_cmd_hdr {
+       u8 cmd_type;
+       u8 seq_no;
+       __le16 total_len;
+       __le32 driver_handler;
+};
+
+struct wilc_cfg_frame {
+       struct wilc_cfg_cmd_hdr hdr;
+       u8 frame[WILC_MAX_CFG_FRAME_SIZE];
+};
+
+struct wilc_cfg_rsp {
+       u8 type;
+       u8 seq_no;
+};
+
+struct wilc;
+struct wilc_vif;
+
+int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
+                               u32 buffer_size);
+int wilc_wlan_start(struct wilc *wilc);
+int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif);
+int txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
+                             u32 buffer_size,
+                             void (*tx_complete_fn)(void *, int));
+int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count);
+void wilc_handle_isr(struct wilc *wilc);
+void wilc_wlan_cleanup(struct net_device *dev);
+int cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
+                     u32 buffer_size, int commit, u32 drv_handler);
+int cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
+                     u32 drv_handler);
+int cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size);
+int txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+                              u32 buffer_size,
+                              void (*tx_complete_fn)(void *, int));
+
+void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value);
+int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc);
+netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *dev);
+
+void wilc_wfi_p2p_rx(struct net_device *dev, u8 *buff, u32 size);
+void host_wakeup_notify(struct wilc *wilc, int source);
+void host_sleep_notify(struct wilc *wilc, int source);
+void chip_allow_sleep(struct wilc *wilc, int source);
+void chip_wakeup(struct wilc *wilc, int source);
+int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
+                        u32 count, u32 drv);
+void wilc_wlan_power_on_sequence(struct wilc *wilc);
+void wilc_wlan_power_off_sequence(struct wilc *wilc);
+
+void wilc_bt_init(struct wilc *wilc);
+void wilc_bt_deinit(void);
+#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
+void eap_buff_timeout(struct timer_list *t);
+#else
+void eap_buff_timeout(unsigned long user);
+#endif
+void acquire_bus(struct wilc *wilc, enum bus_acquire acquire, int source);
+void release_bus(struct wilc *wilc, enum bus_release release, int source);
+int wilc_wlan_init(struct net_device *dev);
+u32 wilc_get_chipid(struct wilc *wilc, bool update);
+void wilc_wfi_handle_monitor_rx(struct wilc *wilc, u8 *buff, u32 size);
+#endif
diff --git a/drivers/net/wireless/mchp/wilc_wlan_cfg.c b/drivers/net/wireless/mchp/wilc_wlan_cfg.c
new file mode 100644 (file)
index 0000000..2ee45fa
--- /dev/null
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include "wilc_wlan_if.h"
+#include "wilc_wlan.h"
+#include "wilc_wlan_cfg.h"
+#include "wilc_wfi_netdevice.h"
+
+enum cfg_cmd_type {
+       CFG_BYTE_CMD    = 0,
+       CFG_HWORD_CMD   = 1,
+       CFG_WORD_CMD    = 2,
+       CFG_STR_CMD     = 3,
+       CFG_BIN_CMD     = 4
+};
+
+static struct wilc_cfg_byte g_cfg_byte[] = {
+       {WID_STATUS, 0},
+       {WID_RSSI, 0},
+       {WID_LINKSPEED, 0},
+       {WID_TX_POWER, 0},
+       {WID_WOWLAN_TRIGGER, 0},
+       {WID_NIL, 0}
+};
+
+static struct wilc_cfg_hword g_cfg_hword[] = {
+       {WID_NIL, 0}
+};
+
+static struct wilc_cfg_word g_cfg_word[] = {
+       {WID_FAILED_COUNT, 0},
+       {WID_RECEIVED_FRAGMENT_COUNT, 0},
+       {WID_SUCCESS_FRAME_COUNT, 0},
+       {WID_GET_INACTIVE_TIME, 0},
+       {WID_NIL, 0}
+
+};
+
+static struct wilc_cfg_str g_cfg_str[] = {
+       {WID_FIRMWARE_VERSION, NULL},
+       {WID_MAC_ADDR, NULL},
+       {WID_ASSOC_RES_INFO, NULL},
+       {WID_NIL, NULL}
+};
+
+static struct wilc_cfg_bin g_cfg_bin[] = {
+       {WID_ANTENNA_SELECTION, NULL},
+       {WID_NIL, NULL}
+};
+
+/********************************************
+ *
+ *      Configuration Functions
+ *
+ ********************************************/
+
+static int wilc_wlan_cfg_set_byte(u8 *frame, u32 offset, u16 id, u8 val8)
+{
+       u8 *buf;
+
+       if ((offset + 4) >= WILC_MAX_CFG_FRAME_SIZE)
+               return 0;
+
+       buf = &frame[offset];
+
+       buf[0] = (u8)id;
+       buf[1] = (u8)(id >> 8);
+       buf[2] = 1;
+       buf[3] = 0;
+       buf[4] = val8;
+       return 5;
+}
+
+static int wilc_wlan_cfg_set_hword(u8 *frame, u32 offset, u16 id, u16 val16)
+{
+       u8 *buf;
+
+       if ((offset + 5) >= WILC_MAX_CFG_FRAME_SIZE)
+               return 0;
+
+       buf = &frame[offset];
+
+       buf[0] = (u8)id;
+       buf[1] = (u8)(id >> 8);
+       buf[2] = 2;
+       buf[3] = 0;
+       buf[4] = (u8)val16;
+       buf[5] = (u8)(val16 >> 8);
+
+       return 6;
+}
+
+static int wilc_wlan_cfg_set_word(u8 *frame, u32 offset, u16 id, u32 val32)
+{
+       u8 *buf;
+
+       if ((offset + 7) >= WILC_MAX_CFG_FRAME_SIZE)
+               return 0;
+
+       buf = &frame[offset];
+
+       buf[0] = (u8)id;
+       buf[1] = (u8)(id >> 8);
+       buf[2] = 4;
+       buf[3] = 0;
+       buf[4] = (u8)val32;
+       buf[5] = (u8)(val32 >> 8);
+       buf[6] = (u8)(val32 >> 16);
+       buf[7] = (u8)(val32 >> 24);
+
+       return 8;
+}
+
+static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str,
+                                u32 size)
+{
+       u8 *buf;
+
+       if ((offset + size + 4) >= WILC_MAX_CFG_FRAME_SIZE)
+               return 0;
+
+       buf = &frame[offset];
+
+       buf[0] = (u8)id;
+       buf[1] = (u8)(id >> 8);
+       buf[2] = (u8)size;
+       buf[3] = (u8)(size >> 8);
+
+       if (str && size != 0)
+               memcpy(&buf[4], str, size);
+
+       return (size + 4);
+}
+
+static int wilc_wlan_cfg_set_bin(u8 *frame, u32 offset, u16 id, u8 *b, u32 size)
+{
+       u8 *buf;
+       u32 i;
+       u8 checksum = 0;
+
+       if ((offset + size + 5) >= WILC_MAX_CFG_FRAME_SIZE)
+               return 0;
+
+       buf = &frame[offset];
+       buf[0] = (u8)id;
+       buf[1] = (u8)(id >> 8);
+       buf[2] = (u8)size;
+       buf[3] = (u8)(size >> 8);
+
+       if ((b) && size != 0) {
+               memcpy(&buf[4], b, size);
+               for (i = 0; i < size; i++)
+                       checksum += buf[i + 4];
+       }
+
+       buf[size + 4] = checksum;
+
+       return (size + 5);
+}
+
+/********************************************
+ *
+ *      Configuration Response Functions
+ *
+ ********************************************/
+
+#define GET_WID_TYPE(wid)              (((wid) >> 12) & 0x7)
+static void wilc_wlan_parse_response_frame(struct wilc *wl, u8 *info,
+                                          int size)
+{
+       u16 wid;
+       u32 len = 0, i = 0;
+       struct wilc_vif *vif = wl->vif[0];
+
+       while (size > 0) {
+               i = 0;
+               wid = get_unaligned_le16(info);
+
+               PRINT_D(vif->ndev, GENERIC_DBG, "Processing response for %d\n",
+                       wid);
+               switch (GET_WID_TYPE(wid)) {
+               case WID_CHAR:
+                       do {
+                               if (wl->cfg.b[i].id == WID_NIL)
+                                       break;
+
+                               if (wl->cfg.b[i].id == wid) {
+                                       wl->cfg.b[i].val = info[4];
+                                       break;
+                               }
+                               i++;
+                       } while (1);
+                       len = 3;
+                       break;
+
+               case WID_SHORT:
+                       do {
+                               struct wilc_cfg_hword *hw = &wl->cfg.hw[i];
+
+                               if (hw->id == WID_NIL)
+                                       break;
+
+                               if (hw->id == wid) {
+                                       hw->val = get_unaligned_le16(&info[4]);
+                                       break;
+                               }
+                               i++;
+                       } while (1);
+                       len = 4;
+                       break;
+
+               case WID_INT:
+                       do {
+                               struct wilc_cfg_word *w = &wl->cfg.w[i];
+
+                               if (w->id == WID_NIL)
+                                       break;
+
+                               if (w->id == wid) {
+                                       w->val = get_unaligned_le32(&info[4]);
+                                       break;
+                               }
+                               i++;
+                       } while (1);
+                       len = 6;
+                       break;
+
+               case WID_STR:
+                       do {
+                               if (wl->cfg.s[i].id == WID_NIL)
+                                       break;
+
+                               if (wl->cfg.s[i].id == wid) {
+                                       memcpy(wl->cfg.s[i].str, &info[2],
+                                              (2+((info[3] << 8) | info[2])));
+                                       break;
+                               }
+                               i++;
+                       } while (1);
+                       len = 2+((info[3] << 8) | info[2]);
+                       break;
+               case WID_BIN_DATA:
+                       do {
+                               u16 length = (info[3] << 8) | info[2];
+                               u8 checksum = 0;
+                               int j = 0;
+
+                               if (wl->cfg.bin[i].id == WID_NIL)
+                                       break;
+
+                               if (wl->cfg.bin[i].id != wid) {
+                                       i++;
+                                       continue;
+                               }
+
+                               /*
+                                * Compute the Checksum of received
+                                * data field
+                                */
+                               for (j = 0; j < length; j++)
+                                       checksum += info[4 + j];
+                               /*
+                                * Verify the checksum of recieved BIN
+                                * DATA
+                                */
+                               if (checksum != info[4 + length]) {
+                                       PRINT_ER(vif->ndev, "Checksum Failed");
+                                       return;
+                               }
+
+                               memcpy(wl->cfg.bin[i].bin, &info[2], length+2);
+                               /*
+                                * value length + data length +
+                                * checksum
+                                */
+                               len = 2 + length + 1;
+                               break;
+
+                       } while (1);
+                       break;
+               default:
+                       break;
+               }
+               size -= (2 + len);
+               info += (2 + len);
+       }
+}
+
+static void wilc_wlan_parse_info_frame(struct wilc *wl, u8 *info)
+{
+       struct wilc_vif *vif = wl->vif[0];
+       u32 wid, len;
+
+       wid = get_unaligned_le16(info);
+
+       len = info[2];
+       PRINT_D(vif->ndev, GENERIC_DBG, "Status Len = %d Id= %d\n", len, wid);
+
+       if (len == 1 && wid == WID_STATUS) {
+               int i = 0;
+
+               do {
+                       if (wl->cfg.b[i].id == WID_NIL)
+                               break;
+
+                       if (wl->cfg.b[i].id == wid) {
+                               wl->cfg.b[i].val = info[3];
+                               break;
+                       }
+                       i++;
+               } while (1);
+       }
+}
+
+/********************************************
+ *
+ *      Configuration Exported Functions
+ *
+ ********************************************/
+
+int cfg_set_wid(struct wilc_vif *vif, u8 *frame, u32 offset, u16 id, u8 *buf,
+                         int size)
+{
+       u8 type = (id >> 12) & 0xf;
+       int ret = 0;
+
+       switch (type) {
+       case CFG_BYTE_CMD:
+               if (size >= 1)
+                       ret = wilc_wlan_cfg_set_byte(frame, offset, id, *buf);
+               break;
+
+       case CFG_HWORD_CMD:
+               if (size >= 2)
+                       ret = wilc_wlan_cfg_set_hword(frame, offset, id,
+                                                     *((u16 *)buf));
+               break;
+
+       case CFG_WORD_CMD:
+               if (size >= 4)
+                       ret = wilc_wlan_cfg_set_word(frame, offset, id,
+                                                    *((u32 *)buf));
+               break;
+
+       case CFG_STR_CMD:
+               ret = wilc_wlan_cfg_set_str(frame, offset, id, buf, size);
+               break;
+
+       case CFG_BIN_CMD:
+               ret = wilc_wlan_cfg_set_bin(frame, offset, id, buf, size);
+               break;
+       default:
+               PRINT_ER(vif->ndev, "illegal id\n");
+       }
+
+       return ret;
+}
+
+int cfg_get_wid(u8 *frame, u32 offset, u16 id)
+{
+       u8 *buf;
+
+       if ((offset + 2) >= WILC_MAX_CFG_FRAME_SIZE)
+               return 0;
+
+       buf = &frame[offset];
+
+       buf[0] = (u8)id;
+       buf[1] = (u8)(id >> 8);
+
+       return 2;
+}
+
+int cfg_get_wid_value(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size)
+{
+       u32 type = (wid >> 12) & 0xf;
+       int i, ret = 0;
+
+       i = 0;
+       if (type == CFG_BYTE_CMD) {
+               do {
+                       if (wl->cfg.b[i].id == WID_NIL)
+                               break;
+
+                       if (wl->cfg.b[i].id == wid) {
+                               memcpy(buffer,  &wl->cfg.b[i].val, 1);
+                               ret = 1;
+                               break;
+                       }
+                       i++;
+               } while (1);
+       } else if (type == CFG_HWORD_CMD) {
+               do {
+                       if (wl->cfg.hw[i].id == WID_NIL)
+                               break;
+
+                       if (wl->cfg.hw[i].id == wid) {
+                               memcpy(buffer,  &wl->cfg.hw[i].val, 2);
+                               ret = 2;
+                               break;
+                       }
+                       i++;
+               } while (1);
+       } else if (type == CFG_WORD_CMD) {
+               do {
+                       if (wl->cfg.w[i].id == WID_NIL)
+                               break;
+
+                       if (wl->cfg.w[i].id == wid) {
+                               memcpy(buffer,  &wl->cfg.w[i].val, 4);
+                               ret = 4;
+                               break;
+                       }
+                       i++;
+               } while (1);
+       } else if (type == CFG_STR_CMD) {
+               do {
+                       u32 id = wl->cfg.s[i].id;
+
+                       if (id == WID_NIL)
+                               break;
+
+                       if (id == wid) {
+                               u16 size = get_unaligned_le16(wl->cfg.s[i].str);
+
+                               if (buffer_size >= size) {
+                                       memcpy(buffer,  &wl->cfg.s[i].str[2],
+                                              size);
+                                       ret = size;
+                               }
+                               break;
+                       }
+                       i++;
+               } while (1);
+       } else if (type == CFG_BIN_CMD) { /* binary command */
+               do {
+                       if (wl->cfg.bin[i].id == WID_NIL)
+                               break;
+
+                       if (wl->cfg.bin[i].id == wid) {
+                               uint32_t size = wl->cfg.bin[i].bin[0] |
+                                            (wl->cfg.bin[i].bin[1]<<8);
+                               if (buffer_size >= size) {
+                                       memcpy(buffer, &wl->cfg.bin[i].bin[2],
+                                               size);
+                                       ret = size;
+                               }
+                               break;
+                       }
+                       i++;
+               } while (1);
+       } else {
+               PRINT_ER(wl->vif[0]->ndev, "[CFG]: illegal type (%08x)\n", wid);
+       }
+
+       return ret;
+}
+
+void cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+                              struct wilc_cfg_rsp *rsp)
+{
+       u8 msg_type;
+       u8 msg_id;
+
+       msg_type = frame[0];
+       msg_id = frame[1];      /* seq no */
+       frame += 4;
+       size -= 4;
+       rsp->type = 0;
+
+       /*
+        * The valid types of response messages are
+        * 'R' (Response),
+        * 'I' (Information), and
+        * 'N' (Network Information)
+        */
+
+       switch (msg_type) {
+       case 'R':
+               wilc_wlan_parse_response_frame(wilc, frame, size);
+               rsp->type = WILC_CFG_RSP;
+               rsp->seq_no = msg_id;
+               break;
+
+       case 'I':
+               wilc_wlan_parse_info_frame(wilc, frame);
+               rsp->type = WILC_CFG_RSP_STATUS;
+               rsp->seq_no = msg_id;
+               /*call host interface info parse as well*/
+               PRINT_D(wilc->vif[0]->ndev, RX_DBG, "Info message received\n");
+               wilc_gnrl_async_info_received(wilc, frame - 4, size + 4);
+               break;
+
+       case 'N':
+               wilc_network_info_received(wilc, frame - 4, size + 4);
+               break;
+
+       case 'S':
+               PRINT_D(wilc->vif[0]->ndev, RX_DBG,
+                       "Scan Notification Received\n");
+               wilc_scan_complete_received(wilc, frame - 4, size + 4);
+               break;
+
+       default:
+               PRINT_D(wilc->vif[0]->ndev, RX_DBG,
+                       "Receive unknown message %d-%d-%d-%d-%d-%d-%d-%d\n",
+                        frame[0], frame[1], frame[2], frame[3], frame[4],
+                        frame[5], frame[6], frame[7]);
+               rsp->seq_no = msg_id;
+               break;
+       }
+}
+
+int cfg_init(struct wilc *wl)
+{
+       struct wilc_cfg_str_vals *str_vals;
+       struct wilc_bin_vals *bin_vals;
+       int i = 0;
+
+       wl->cfg.b = kmemdup(g_cfg_byte, sizeof(g_cfg_byte), GFP_KERNEL);
+       if (!wl->cfg.b)
+               return -ENOMEM;
+
+       wl->cfg.hw = kmemdup(g_cfg_hword, sizeof(g_cfg_hword), GFP_KERNEL);
+       if (!wl->cfg.hw)
+               goto out_b;
+
+       wl->cfg.w = kmemdup(g_cfg_word, sizeof(g_cfg_word), GFP_KERNEL);
+       if (!wl->cfg.w)
+               goto out_hw;
+
+       wl->cfg.s = kmemdup(g_cfg_str, sizeof(g_cfg_str), GFP_KERNEL);
+       if (!wl->cfg.s)
+               goto out_w;
+
+       str_vals = kzalloc(sizeof(*str_vals), GFP_KERNEL);
+       if (!str_vals)
+               goto out_s;
+
+
+       wl->cfg.bin = kmemdup(g_cfg_bin, sizeof(g_cfg_bin), GFP_KERNEL);
+       if (!wl->cfg.bin)
+               goto out_str_val;
+
+       bin_vals = kzalloc(sizeof(*bin_vals), GFP_KERNEL);
+       if (!bin_vals)
+               goto out_bin;
+
+       /* store the string cfg parameters */
+       wl->cfg.str_vals = str_vals;
+       wl->cfg.s[i].id = WID_FIRMWARE_VERSION;
+       wl->cfg.s[i].str = str_vals->firmware_version;
+       i++;
+       wl->cfg.s[i].id = WID_MAC_ADDR;
+       wl->cfg.s[i].str = str_vals->mac_address;
+       i++;
+       wl->cfg.s[i].id = WID_ASSOC_RES_INFO;
+       wl->cfg.s[i].str = str_vals->assoc_rsp;
+       i++;
+       wl->cfg.s[i].id = WID_NIL;
+       wl->cfg.s[i].str = NULL;
+
+       /* store the bin parameters */
+       i = 0;
+       wl->cfg.bin[i].id = WID_ANTENNA_SELECTION;
+       wl->cfg.bin[i].bin = bin_vals->antenna_param;
+       i++;
+
+       wl->cfg.bin[i].id = WID_NIL;
+       wl->cfg.bin[i].bin = NULL;
+
+       return 0;
+
+out_bin:
+       kfree(wl->cfg.bin);
+out_str_val:
+       kfree(str_vals);
+out_s:
+       kfree(wl->cfg.s);
+out_w:
+       kfree(wl->cfg.w);
+out_hw:
+       kfree(wl->cfg.hw);
+out_b:
+       kfree(wl->cfg.b);
+       return -ENOMEM;
+}
+
+void cfg_deinit(struct wilc *wl)
+{
+       kfree(wl->cfg.b);
+       kfree(wl->cfg.hw);
+       kfree(wl->cfg.w);
+       kfree(wl->cfg.s);
+       kfree(wl->cfg.str_vals);
+       kfree(wl->cfg.bin);
+       kfree(wl->cfg.bin_vals);
+}
+
diff --git a/drivers/net/wireless/mchp/wilc_wlan_cfg.h b/drivers/net/wireless/mchp/wilc_wlan_cfg.h
new file mode 100644 (file)
index 0000000..3f78a15
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WLAN_CFG_H
+#define WILC_WLAN_CFG_H
+
+struct wilc_cfg_byte {
+       u16 id;
+       u8 val;
+};
+
+struct wilc_cfg_hword {
+       u16 id;
+       u16 val;
+};
+
+struct wilc_cfg_word {
+       u32 id;
+       u32 val;
+};
+
+struct wilc_cfg_str {
+       u16 id;
+       u8 *str;
+};
+
+struct wilc_cfg_bin {
+       u16 id;
+       u8 *bin;
+};
+
+struct wilc_cfg_str_vals {
+       u8 mac_address[7];
+       u8 firmware_version[129];
+       u8 assoc_rsp[256];
+};
+
+struct wilc_bin_vals {
+       u8 antenna_param[5];
+};
+
+struct wilc_cfg {
+       struct wilc_cfg_byte *b;
+       struct wilc_cfg_hword *hw;
+       struct wilc_cfg_word *w;
+       struct wilc_cfg_str *s;
+       struct wilc_cfg_str_vals *str_vals;
+       struct wilc_cfg_bin *bin;
+       struct wilc_bin_vals *bin_vals;
+};
+
+struct wilc;
+int cfg_set_wid(struct wilc_vif *vif, u8 *frame, u32 offset, u16 id, u8 *buf,
+               int size);
+int cfg_get_wid(u8 *frame, u32 offset, u16 id);
+int cfg_get_wid_value(struct wilc *wl, u16 wid, u8 *buffer, u32 buffer_size);
+void cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+                              struct wilc_cfg_rsp *rsp);
+int cfg_init(struct wilc *wl);
+void cfg_deinit(struct wilc *wl);
+
+#endif
diff --git a/drivers/net/wireless/mchp/wilc_wlan_if.h b/drivers/net/wireless/mchp/wilc_wlan_if.h
new file mode 100644 (file)
index 0000000..2bf853b
--- /dev/null
@@ -0,0 +1,817 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_WLAN_IF_H
+#define WILC_WLAN_IF_H
+
+#include <linux/netdevice.h>
+#include "wilc_debugfs.h"
+
+/********************************************
+ *
+ *      Host Interface Defines
+ *
+ ********************************************/
+
+#define        FW_WILC1000_WIFi                "mchp/wilc1000_wifi_firmware.bin"
+#define        FW_WILC3000_WIFI                "mchp/wilc3000_wifi_firmware.bin"
+#define        FW_WILC3000_BLE         "mchp/wilc3000_ble_firmware.bin"
+
+enum bss_types {
+       WILC_FW_BSS_TYPE_INFRA = 0,
+       WILC_FW_BSS_TYPE_INDEPENDENT,
+       WILC_FW_BSS_TYPE_AP,
+};
+
+enum {
+       WILC_FW_OPER_MODE_B_ONLY = 0,    /* 1, 2 M, otherwise 5, 11 M */
+       WILC_FW_OPER_MODE_G_ONLY,        /* 6,12,24 otherwise 9,18,36,48,54 */
+       WILC_FW_OPER_MODE_G_MIXED_11B_1, /* 1,2,5.5,11 otherwise all on */
+       WILC_FW_OPER_MODE_G_MIXED_11B_2, /* 1,2,5,11,6,12,24 otherwise all on */
+};
+
+enum {
+       WILC_FW_PREAMBLE_SHORT = 0,     /* Short Preamble */
+       WILC_FW_PREAMBLE_LONG = 1,      /* Long Preamble */
+       WILC_FW_PREAMBLE_AUTO = 2,      /* Auto Preamble Selection */
+};
+
+#define DEV_WIFI       0
+#define DEV_BT         1
+#define DEV_MAX                2
+
+enum {
+       WILC_FW_PASSIVE_SCAN = 0,
+       WILC_FW_ACTIVE_SCAN = 1,
+};
+
+enum {
+       WILC_FW_NO_POWERSAVE = 0,
+       WILC_FW_MIN_FAST_PS = 1,
+       WILC_FW_MAX_FAST_PS = 2,
+       WILC_FW_MIN_PSPOLL_PS = 3,
+       WILC_FW_MAX_PSPOLL_PS = 4
+};
+
+enum bus_acquire {
+       WILC_BUS_ACQUIRE_ONLY = 0,
+       WILC_BUS_ACQUIRE_AND_WAKEUP = 1,
+};
+
+enum bus_release {
+       WILC_BUS_RELEASE_ONLY = 0,
+       WILC_BUS_RELEASE_ALLOW_SLEEP = 1,
+};
+
+enum {
+       WILC_FW_NO_ENCRYPT = 0,
+       WILC_FW_ENCRYPT_ENABLED = BIT(0),
+       WILC_FW_WEP = BIT(1),
+       WILC_FW_WEP_EXTENDED = BIT(2),
+       WILC_FW_WPA = BIT(3),
+       WILC_FW_WPA2 = BIT(4),
+       WILC_FW_AES = BIT(5),
+       WILC_FW_TKIP = BIT(6)
+};
+
+enum {
+       WILC_FW_SEC_NO = WILC_FW_NO_ENCRYPT,
+       WILC_FW_SEC_WEP = WILC_FW_WEP | WILC_FW_ENCRYPT_ENABLED,
+       WILC_FW_SEC_WEP_EXTENDED = WILC_FW_WEP_EXTENDED | WILC_FW_SEC_WEP,
+       WILC_FW_SEC_WPA = WILC_FW_WPA | WILC_FW_ENCRYPT_ENABLED,
+       WILC_FW_SEC_WPA_AES = WILC_FW_AES | WILC_FW_SEC_WPA,
+       WILC_FW_SEC_WPA_TKIP = WILC_FW_TKIP | WILC_FW_SEC_WPA,
+       WILC_FW_SEC_WPA2 = WILC_FW_WPA2 | WILC_FW_ENCRYPT_ENABLED,
+       WILC_FW_SEC_WPA2_AES = WILC_FW_AES | WILC_FW_SEC_WPA2,
+       WILC_FW_SEC_WPA2_TKIP = WILC_FW_TKIP | WILC_FW_SEC_WPA2
+};
+
+enum authtype {
+       WILC_FW_AUTH_OPEN_SYSTEM = 1,
+       WILC_FW_AUTH_SHARED_KEY = 2,
+       WILC_FW_AUTH_ANY = 3,
+       WILC_FW_AUTH_IEEE8021 = 5
+};
+
+enum site_survey {
+       WILC_FW_SITE_SURVEY_1CH = 0,
+       WILC_FW_SITE_SURVEY_ALL_CH = 1,
+       WILC_FW_SITE_SURVEY_OFF = 2
+};
+
+enum {
+       WILC_FW_ACK_POLICY_NORMAL = 0,
+       WILC_FW_ACK_NO_POLICY,
+};
+
+enum {
+       WILC_FW_REKEY_POLICY_DISABLE = 1,
+       WILC_FW_REKEY_POLICY_TIME_BASE,
+       WILC_FW_REKEY_POLICY_PKT_BASE,
+       WILC_FW_REKEY_POLICY_TIME_PKT_BASE
+};
+
+enum {
+       WILC_FW_FILTER_NO = 0x00,
+       WILC_FW_FILTER_AP_ONLY = 0x01,
+       WILC_FW_FILTER_STA_ONLY = 0x02
+};
+
+enum {
+       WILC_FW_11N_PROT_AUTO = 0,      /* Auto */
+       WILC_FW_11N_NO_PROT,            /* Do not use any protection */
+       WILC_FW_11N_PROT_ERP,           /* Protect all ERP frame exchanges */
+       WILC_FW_11N_PROT_HT,            /* Protect all HT frame exchanges  */
+       WILC_FW_11N_PROT_GF             /* Protect all GF frame exchanges  */
+};
+
+enum {
+       WILC_FW_ERP_PROT_SELF_CTS,
+       WILC_FW_ERP_PROT_RTS_CTS,
+};
+
+enum {
+       WILC_FW_11N_OP_MODE_HT_MIXED = 1,
+       WILC_FW_11N_OP_MODE_HT_ONLY_20MHZ,
+       WILC_FW_11N_OP_MODE_HT_ONLY_20_40MHZ,
+};
+
+enum {
+       WILC_FW_OBBS_NONHT_NO_DETECT = 0,
+       WILC_FW_OBBS_NONHT_DETECT_ONLY = 1,
+       WILC_FW_OBBS_NONHT_DETECT_PROTECT = 2,
+       WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT = 3,
+};
+
+enum {
+       WILC_FW_HT_PROT_RTS_CTS_NONHT = 0,  /* RTS-CTS at non-HT rate */
+       WILC_FW_HT_PROT_FIRST_FRAME_NONHT,  /* First frame at non-HT rate */
+       WILC_FW_HT_PROT_LSIG_TXOP,          /* LSIG TXOP Protection */
+       WILC_FW_HT_PROT_FIRST_FRAME_MIXED,  /* First frame at Mixed format */
+};
+
+enum {
+       WILC_FW_SMPS_MODE_STATIC = 1,
+       WILC_FW_SMPS_MODE_DYNAMIC = 2,
+       WILC_FW_SMPS_MODE_MIMO = 3,     /* power save disable */
+};
+
+enum {
+       WILC_FW_TX_RATE_AUTO = 0,
+       WILC_FW_TX_RATE_MBPS_1 = 1,
+       WILC_FW_TX_RATE_MBPS_2 = 2,
+       WILC_FW_TX_RATE_MBPS_5_5 = 5,
+       WILC_FW_TX_RATE_MBPS_11 = 11,
+       WILC_FW_TX_RATE_MBPS_6 = 6,
+       WILC_FW_TX_RATE_MBPS_9 = 9,
+       WILC_FW_TX_RATE_MBPS_12 = 12,
+       WILC_FW_TX_RATE_MBPS_18 = 18,
+       WILC_FW_TX_RATE_MBPS_24 = 24,
+       WILC_FW_TX_RATE_MBPS_36 = 36,
+       WILC_FW_TX_RATE_MBPS_48 = 48,
+       WILC_FW_TX_RATE_MBPS_54 = 54
+};
+
+enum {
+       WILC_FW_DEFAULT_SCAN = 0,
+       WILC_FW_USER_SCAN = BIT(0),
+       WILC_FW_OBSS_PERIODIC_SCAN = BIT(1),
+       WILC_FW_OBSS_ONETIME_SCAN = BIT(2)
+};
+
+enum {
+       WILC_FW_ACTION_FRM_IDX = 0,
+       WILC_FW_PROBE_REQ_IDX = 1
+};
+
+enum wid_type {
+       WID_CHAR                = 0,
+       WID_SHORT               = 1,
+       WID_INT                 = 2,
+       WID_STR                 = 3,
+       WID_BIN_DATA            = 4,
+       WID_BIN                 = 5,
+};
+
+enum {
+       ANTENNA1                = 0,
+       ANTENNA2                = 1,
+       DIVERSITY               = 2,
+       NUM_ANT_MODE
+};
+
+struct wid {
+       u16 id;
+       enum wid_type type;
+       s32 size;
+       s8 *val;
+};
+
+enum {
+       WID_NIL                         = 0xffff,
+
+       /*
+        *  BSS Type
+        *  -----------------------------------------------------------
+        *  Configuration : Infrastructure   Independent   Access Point
+        *  Values to set :         0               1            2
+        *  -----------------------------------------------------------
+        */
+       WID_BSS_TYPE                    = 0x0000,
+
+       /*
+        *  Transmit Rate
+        *  -----------------------------------------------------------
+        *  Configuration : 1  2  5.5  11  6  9  12  18  24  36  48  54
+        *  Values to set : 1  2    5  11  6  9  12  18  24  36  48  54
+        *  -----------------------------------------------------------
+        */
+       WID_CURRENT_TX_RATE             = 0x0001,
+
+       /*
+        *  Channel
+        *  -----------------------------------------------------------
+        *  Configuration(g) : 1  2  3  4  5  6  7  8  9 10 11 12 13 14
+        *  Values to set    : 1  2  3  4  5  6  7  8  9 10 11 12 13 14
+        *  -----------------------------------------------------------
+        */
+       WID_CURRENT_CHANNEL             = 0x0002,
+
+       /*
+        *  Preamble
+        *  -----------------------------------------------------------
+        *  Configuration :    short    long      Auto
+        *  Values to set :       0       1         2
+        *  -----------------------------------------------------------
+        */
+       WID_PREAMBLE                    = 0x0003,
+
+       /*
+        * 11g operating mode (ignored if 11g not present)
+        *  -----------------------------------------------------------
+        *  Configuration :   HighPerf  Compat(RSet #1) Compat(RSet #2)
+        *  Values to set :          1               2               3
+        *  -----------------------------------------------------------
+        */
+       WID_11G_OPERATING_MODE          = 0x0004,
+
+       /*
+        *  Mac status (response only)
+        *  -----------------------------------------------------------
+        *  Configuration :   disconnect  connect
+        *  Values to get :          0       1
+        *  -----------------------------------------------------------
+        */
+       WID_STATUS                      = 0x0005,
+
+       /*
+        *  Scan type
+        *  -----------------------------------------------------------
+        *  Configuration :   Passive Scanning   Active Scanning
+        *  Values to set :                  0                 1
+        *  -----------------------------------------------------------
+        */
+       WID_SCAN_TYPE                   = 0x0007,
+
+       /*
+        *  Key Id (WEP default key Id)
+        *  -----------------------------------------------------------
+        *  Configuration :   Any value between 0 to 3
+        *  Values to set :   Same value. Default is 0
+        *  -----------------------------------------------------------
+        */
+       WID_KEY_ID                      = 0x0009,
+
+       /*
+        *  QoS Enable
+        *  -----------------------------------------------------------
+        *  Configuration :   QoS Disable   WMM Enable
+        *  Values to set :   0             1
+        *  -----------------------------------------------------------
+        */
+       WID_QOS_ENABLE                  = 0x000A,
+
+       /*
+        *  Power Management
+        *  -----------------------------------------------------------
+        *  Configuration : NO_POWERSAVE MIN_POWERSAVE MAX_POWERSAVE
+        *  Values to set : 0            1             2
+        *  -----------------------------------------------------------
+        */
+       WID_POWER_MANAGEMENT            = 0x000B,
+
+       /*
+        *  WEP/802 11I Configuration
+        *  -----------------------------------------------------------
+        *  Configuration:Disable WP40 WP104 WPA-AES WPA-TKIP RSN-AES RSN-TKIP
+        *  Values (0x)  :   00     03   07     29       49       31      51
+        *  Configuration:WPA-AES+TKIP RSN-AES+TKIP
+        *  Values (0x)  :      69        71
+        *  -----------------------------------------------------------
+        */
+       WID_11I_MODE                    = 0x000C,
+
+       /*
+        *  WEP Configuration: Used in BSS STA mode only when WEP is enabled
+        *  -----------------------------------------------------------
+        *  Configuration : Open System Shared Key Any Type | 802.1x Auth
+        *  Values (0x)   :    01             02         03 |    BIT2
+        *  -----------------------------------------------------------
+        */
+       WID_AUTH_TYPE                   = 0x000D,
+
+       /*
+        *  Site Survey Type
+        *  -----------------------------------------------------------
+        *  Configuration       :  Values to set
+        *  Survey 1 Channel    :  0
+        *  survey all Channels :  1
+        *  Disable Site Survey :  2
+        *  -----------------------------------------------------------
+        */
+       WID_SITE_SURVEY                 = 0x000E,
+
+       /*
+        *  Listen Interval
+        *  -----------------------------------------------------------
+        *  Configuration :   Any value between 1 to 255
+        *  Values to set :   Same value. Default is 3
+        *  -----------------------------------------------------------
+        */
+       WID_LISTEN_INTERVAL             = 0x000F,
+
+       /*
+        *  DTIM Period
+        *  -----------------------------------------------------------
+        *  Configuration :   Any value between 1 to 255
+        *  Values to set :   Same value. Default is 3
+        *  -----------------------------------------------------------
+        */
+       WID_DTIM_PERIOD                 = 0x0010,
+
+       /*
+        *  ACK Policy
+        *  -----------------------------------------------------------
+        *  Configuration :   Normal Ack            No Ack
+        *  Values to set :       0                   1
+        *  -----------------------------------------------------------
+        */
+       WID_ACK_POLICY                  = 0x0011,
+
+       /*
+        *  Reset MAC (Set only)
+        *  -----------------------------------------------------------
+        *  Configuration :   Don't Reset       Reset   No Request
+        *  Values to set :       0               1         2
+        *  -----------------------------------------------------------
+        */
+       WID_RESET                       = 0x0012,
+
+       /*
+        *  Broadcast SSID Option: Setting this will adhere to "" SSID element
+        *  -----------------------------------------------------------
+        *  Configuration :   Enable             Disable
+        *  Values to set :   1                  0
+        *  -----------------------------------------------------------
+        */
+       WID_BCAST_SSID                  = 0x0015,
+
+       /*
+        *  Disconnect (Station)
+        *  -----------------------------------------------------------
+        *  Configuration :   Association ID
+        *  Values to set :   Association ID
+        *  -----------------------------------------------------------
+        */
+       WID_DISCONNECT                  = 0x0016,
+
+       /*
+        *  11a Tx Power Level
+        *  -----------------------------------------------------------
+        *  Configuration : Sets TX Power (Higher the value greater the power)
+        *  Values to set : Any value between 0 and 63 (inclusive Default 48)
+        *  -----------------------------------------------------------
+        */
+       WID_TX_POWER_LEVEL_11A          = 0x0018,
+
+       /*
+        *  Group Key Update Policy Selection
+        *  -----------------------------------------------------------
+        *  Configuration : Disabled timeBased packetBased timePacketBased
+        *  Values to set :   1            2          3              4
+        *  -----------------------------------------------------------
+        */
+       WID_REKEY_POLICY                = 0x0019,
+
+       /*
+        *  Allow Short Slot
+        *  -----------------------------------------------------------
+        *  Configuration : Disallow Short Slot      Allow Short Slot
+        *          (Enable Only Long Slot) (Enable Short Slot if applicable)
+        *  Values to set :    0         1
+        *  -----------------------------------------------------------
+        */
+       WID_SHORT_SLOT_ALLOWED          = 0x001A,
+
+       WID_PHY_ACTIVE_REG              = 0x001B,
+
+       /*
+        *  11b Tx Power Level
+        *  -----------------------------------------------------------
+        *  Configuration : Sets TX Power (Higher the value greater the power)
+        *  Values to set : Any value between 0 and 63 (inclusive Default 48)
+        *  -----------------------------------------------------------
+        */
+       WID_TX_POWER_LEVEL_11B          = 0x001D,
+
+       /*
+        *  Scan Request
+        *  -----------------------------------------------------------
+        *  Configuration : Request default scan
+        *  Values to set : 0
+        *  -----------------------------------------------------------
+        */
+       WID_START_SCAN_REQ              = 0x001E,
+
+       /*
+        *  Rssi (get only)
+        *  -----------------------------------------------------------
+        *  Configuration :
+        *  Values to get : Rssi value
+        *  -----------------------------------------------------------
+        */
+       WID_RSSI                        = 0x001F,
+
+       /*
+        * Join Request
+        *  -----------------------------------------------------------
+        *  Configuration : Request to join
+        *  Values to set : index of scan result
+        *  -----------------------------------------------------------
+        */
+       WID_JOIN_REQ                    = 0x0020,
+
+       WID_LINKSPEED                   = 0x0026,
+
+       /*
+        *  Enable User Control of TX Power
+        *  -----------------------------------------------------------
+        *  Configuration : Disable                  Enable
+        *  Values to set :    0                       1
+        *  -----------------------------------------------------------
+        */
+       WID_USER_CONTROL_ON_TX_POWER    = 0x0027,
+
+       WID_MEMORY_ACCESS_8BIT          = 0x0029,
+
+       /*
+        *  Enable Auto RX Sensitivity feature
+        *  -----------------------------------------------------------
+        *  Configuration : Disable                  Enable
+        *  Values to set :    0                       1
+        *  -----------------------------------------------------------
+        */
+       WID_AUTO_RX_SENSITIVITY         = 0x0032,
+
+       /*
+        *  Receive Buffer Based Ack
+        *  -----------------------------------------------------------
+        *  Configuration : Disable                  Enable
+        *  Values to set :    0                       1
+        *  -----------------------------------------------------------
+        */
+       WID_DATAFLOW_CONTROL            = 0x0033,
+
+       /*
+        *  Scan Filter
+        *  -----------------------------------------------------------
+        *  Configuration : Class       No filter   AP only   Station Only
+        *  Values to set :                0           1           2
+        *  Configuration : Priority    High Rssi   Low Rssi     Detect
+        *  Values to set :                0          0x4         0x0
+        *  Configuration : Channel     filter off  filter on
+        *  Values to set :                0          0x10
+        *  -----------------------------------------------------------
+        */
+       WID_SCAN_FILTER                 = 0x0036,
+
+       /*
+        *  Link Loss Threshold (measure in the beacon period)
+        *  -----------------------------------------------------------
+        *  Configuration : Any value between 10 and 254(Set to 255 disable)
+        *  Values to set : Same value. Default is 10
+        *  -----------------------------------------------------------
+        */
+       WID_LINK_LOSS_THRESHOLD         = 0x0037,
+
+       WID_ABORT_RUNNING_SCAN          = 0x003E,
+
+       /* NMAC Character WID list */
+       WID_WPS_START                   = 0x0043,
+
+       /*
+        *  Protection mode for MAC
+        *  -----------------------------------------------------------
+        *  Configuration :  Auto  No protection  ERP    HT    GF
+        *  Values to set :  0     1              2      3     4
+        *  -----------------------------------------------------------
+        */
+       WID_11N_PROT_MECH               = 0x0080,
+
+       /*
+        *  ERP Protection type for MAC
+        *  -----------------------------------------------------------
+        *  Configuration :  Self-CTS   RTS-CTS
+        *  Values to set :  0          1
+        *  -----------------------------------------------------------
+        */
+       WID_11N_ERP_PROT_TYPE           = 0x0081,
+
+       /*
+        *  HT Option Enable
+        *  -----------------------------------------------------------
+        *  Configuration :   HT Enable          HT Disable
+        *  Values to set :   1                  0
+        *  -----------------------------------------------------------
+        */
+       WID_11N_ENABLE                  = 0x0082,
+
+       /*
+        *  11n Operating mode (Note that 11g operating mode will also be
+        *  used in addition to this, if this is set to HT Mixed mode)
+        *  -----------------------------------------------------------
+        *   Configuration :  HT Mixed  HT Only-20MHz   HT Only-20/40MHz
+        *  Values to set :     1         2               3
+        *  -----------------------------------------------------------
+        */
+       WID_11N_OPERATING_MODE          = 0x0083,
+
+       /*
+        *  11n OBSS non-HT STA Detection flag
+        *  -----------------------------------------------------------
+        *  Configuration :  Do not detect
+        *  Values to set :  0
+        *  Configuration :  Detect, do not protect or report
+        *  Values to set :  1
+        *  Configuration :  Detect, protect and do not report
+        *  Values to set :  2
+        *  Configuration :  Detect, protect and report to other BSS
+        *  Values to set :  3
+        *  -----------------------------------------------------------
+        */
+       WID_11N_OBSS_NONHT_DETECTION    = 0x0084,
+
+       /*
+        *  11n HT Protection Type
+        *  -----------------------------------------------------------
+        *  Configuration :  RTS-CTS   First Frame Exchange at non-HT-rate
+        *  Values to set :  0         1
+        *  Configuration :  LSIG TXOP First Frame Exchange in Mixed Fmt
+        *  Values to set :  2         3
+        *  -----------------------------------------------------------
+        */
+       WID_11N_HT_PROT_TYPE            = 0x0085,
+
+       /*
+        *  11n RIFS Protection Enable Flag
+        *  -----------------------------------------------------------
+        *  Configuration :  Disable    Enable
+        *  Values to set :  0          1
+        *  -----------------------------------------------------------
+        */
+       WID_11N_RIFS_PROT_ENABLE        = 0x0086,
+
+       /*
+        *  SMPS Mode
+        *  -----------------------------------------------------------
+        *  Configuration :  Static   Dynamic   MIMO (Power Save Disabled)
+        *  Values to set :  1        2         3
+        *  -----------------------------------------------------------
+        */
+       WID_11N_SMPS_MODE               = 0x0087,
+
+       /*
+        *  Current transmit MCS
+        *  -----------------------------------------------------------
+        *  Configuration :  MCS Index for data rate
+        *  Values to set :  0 to 7
+        *  -----------------------------------------------------------
+        */
+       WID_11N_CURRENT_TX_MCS          = 0x0088,
+
+       WID_11N_PRINT_STATS             = 0x0089,
+
+       /*
+        *  11n Short GI Enable Flag
+        *  -----------------------------------------------------------
+        *  Configuration :  Disable    Enable
+        *  Values to set :  0          1
+        *  -----------------------------------------------------------
+        */
+       WID_11N_SHORT_GI_ENABLE         = 0x008D,
+
+       /*
+        *  11n RIFS Enable Flag
+        *  -----------------------------------------------------------
+        *  Configuration :  Disable    Enable
+        *  Values to set :  0          1
+        *  -----------------------------------------------------------
+        */
+       WID_RIFS_MODE                   = 0x0094,
+
+       /*
+        *  TX Abort Feature
+        *  -----------------------------------------------------------
+        *  Configuration :  Disable Self CTS    Enable Self CTS
+        *  Values to set :             0                      1
+        *  Configuration :  Disable TX Abort    Enable TX Abort
+        *  Values to set :             2                      3
+        *  Configuration :  Enable HW TX Abort Enable SW TX Abort
+        *  Values to set :             4                      5
+        *  -----------------------------------------------------------
+        */
+       WID_TX_ABORT_CONFIG             = 0x00A1,
+
+       WID_REG_TSSI_11B_VALUE          = 0x00A6,
+       WID_REG_TSSI_11G_VALUE          = 0x00A7,
+       WID_REG_TSSI_11N_VALUE          = 0x00A8,
+       WID_TX_CALIBRATION              = 0x00A9,
+       WID_DSCR_TSSI_11B_VALUE         = 0x00AA,
+       WID_DSCR_TSSI_11G_VALUE         = 0x00AB,
+       WID_DSCR_TSSI_11N_VALUE         = 0x00AC,
+
+       /*
+        *  Immediate Block-Ack Support
+        *  -----------------------------------------------------------
+        *  Configuration : Disable                  Enable
+        *  Values to set :    0                       1
+        *  -----------------------------------------------------------
+        */
+       WID_11N_IMMEDIATE_BA_ENABLED    = 0x00AF,
+
+       /*
+        *  TXOP Disable Flag
+        *  -----------------------------------------------------------
+        *  Configuration : Disable                  Enable
+        *  Values to set :    1                        0
+        *  -----------------------------------------------------------
+        */
+       WID_11N_TXOP_PROT_DISABLE       = 0x00B0,
+
+       WID_TX_POWER_LEVEL_11N          = 0x00B1,
+
+       /* Custom Character WID list */
+       /* SCAN Complete notification WID*/
+       WID_SCAN_COMPLETE               = 0x00C9,
+
+       WID_DEL_BEACON                  = 0x00CA,
+
+       WID_LOG_TERMINAL_SWITCH         = 0x00CD,
+       WID_TX_POWER                    = 0x00CE,
+       WID_WOWLAN_TRIGGER              = 0X00CF,
+       /*  EMAC Short WID list */
+       /*  RTS Threshold */
+       /*
+        *  -----------------------------------------------------------
+        *  Configuration :   Any value between 256 to 2347
+        *  Values to set :   Same value. Default is 2347
+        *  -----------------------------------------------------------
+        */
+       WID_RTS_THRESHOLD               = 0x1000,
+
+       /*
+        *  Fragmentation Threshold
+        *  -----------------------------------------------------------
+        *  Configuration :   Any value between 256 to 2346
+        *  Values to set :   Same value. Default is 2346
+        *  -----------------------------------------------------------
+        */
+       WID_FRAG_THRESHOLD              = 0x1001,
+
+       WID_SHORT_RETRY_LIMIT           = 0x1002,
+       WID_LONG_RETRY_LIMIT            = 0x1003,
+       WID_BEACON_INTERVAL             = 0x1006,
+       WID_MEMORY_ACCESS_16BIT         = 0x1008,
+       WID_PASSIVE_SCAN_TIME           = 0x100D,
+       WID_JOIN_START_TIMEOUT          = 0x100F,
+       WID_ASOC_TIMEOUT                = 0x1011,
+       WID_11I_PROTOCOL_TIMEOUT        = 0x1012,
+       WID_EAPOL_RESPONSE_TIMEOUT      = 0x1013,
+
+       /* NMAC Short WID list */
+       WID_11N_SIG_QUAL_VAL            = 0x1085,
+       WID_CCA_THRESHOLD               = 0x1087,
+
+       /* Custom Short WID list */
+
+       /* EMAC Integer WID list */
+       WID_FAILED_COUNT                = 0x2000,
+       WID_RETRY_COUNT                 = 0x2001,
+       WID_MULTIPLE_RETRY_COUNT        = 0x2002,
+       WID_FRAME_DUPLICATE_COUNT       = 0x2003,
+       WID_ACK_FAILURE_COUNT           = 0x2004,
+       WID_RECEIVED_FRAGMENT_COUNT     = 0x2005,
+       WID_MCAST_RECEIVED_FRAME_COUNT  = 0x2006,
+       WID_FCS_ERROR_COUNT             = 0x2007,
+       WID_SUCCESS_FRAME_COUNT         = 0x2008,
+       WID_HUT_TX_COUNT                = 0x200A,
+       WID_TX_FRAGMENT_COUNT           = 0x200B,
+       WID_TX_MULTICAST_FRAME_COUNT    = 0x200C,
+       WID_RTS_SUCCESS_COUNT           = 0x200D,
+       WID_RTS_FAILURE_COUNT           = 0x200E,
+       WID_WEP_UNDECRYPTABLE_COUNT     = 0x200F,
+       WID_REKEY_PERIOD                = 0x2010,
+       WID_REKEY_PACKET_COUNT          = 0x2011,
+       WID_1X_SERV_ADDR                = 0x2012,
+       WID_STACK_IP_ADDR               = 0x2013,
+       WID_STACK_NETMASK_ADDR          = 0x2014,
+       WID_HW_RX_COUNT                 = 0x2015,
+       WID_MEMORY_ADDRESS              = 0x201E,
+       WID_MEMORY_ACCESS_32BIT         = 0x201F,
+
+       /* NMAC Integer WID list */
+       /* Custom Integer WID list */
+       WID_GET_INACTIVE_TIME           = 0x2084,
+       WID_SET_OPERATION_MODE          = 0X2086,
+       /* EMAC String WID list */
+       WID_SSID                        = 0x3000,
+       WID_FIRMWARE_VERSION            = 0x3001,
+       WID_OPERATIONAL_RATE_SET        = 0x3002,
+       WID_BSSID                       = 0x3003,
+       WID_WEP_KEY_VALUE               = 0x3004,
+       WID_11I_PSK                     = 0x3008,
+       WID_11E_P_ACTION_REQ            = 0x3009,
+       WID_1X_KEY                      = 0x300A,
+       WID_HARDWARE_VERSION            = 0x300B,
+       WID_MAC_ADDR                    = 0x300C,
+       WID_HUT_DEST_ADDR               = 0x300D,
+       WID_PHY_VERSION                 = 0x300F,
+       WID_SUPP_USERNAME               = 0x3010,
+       WID_SUPP_PASSWORD               = 0x3011,
+       WID_SITE_SURVEY_RESULTS         = 0x3012,
+       WID_RX_POWER_LEVEL              = 0x3013,
+       WID_SET_STA_MAC_INACTIVE_TIME   = 0x3017,
+       WID_ADD_WEP_KEY                 = 0x3019,
+       WID_REMOVE_WEP_KEY              = 0x301A,
+       WID_ADD_PTK                     = 0x301B,
+       WID_ADD_RX_GTK                  = 0x301C,
+       WID_ADD_TX_GTK                  = 0x301D,
+       WID_REMOVE_KEY                  = 0x301E,
+       WID_ASSOC_REQ_INFO              = 0x301F,
+       WID_ASSOC_RES_INFO              = 0x3020,
+       WID_MANUFACTURER                = 0x3026, /*Added for CAPI tool */
+       WID_MODEL_NAME                  = 0x3027, /*Added for CAPI tool */
+       WID_MODEL_NUM                   = 0x3028, /*Added for CAPI tool */
+       WID_DEVICE_NAME                 = 0x3029, /*Added for CAPI tool */
+
+       /* NMAC String WID list */
+       WID_SET_DRV_HANDLER             = 0x3079,
+       WID_11N_P_ACTION_REQ            = 0x3080,
+       WID_HUT_TEST_ID                 = 0x3081,
+       WID_PMKID_INFO                  = 0x3082,
+       WID_FIRMWARE_INFO               = 0x3083,
+       WID_REGISTER_FRAME              = 0x3084,
+       WID_DEL_ALL_STA                 = 0x3085,
+       WID_REMAIN_ON_CHAN              = 0x3996,
+       WID_SSID_PROBE_REQ              = 0x3997,
+       WID_JOIN_REQ_EXTENDED           = 0x3998,
+
+       WID_IP_ADDRESS                  = 0x3999,
+
+       /* Custom String WID list */
+
+       /* EMAC Binary WID list */
+       WID_UAPSD_CONFIG                = 0x4001,
+       WID_UAPSD_STATUS                = 0x4002,
+       WID_WMM_AP_AC_PARAMS            = 0x4003,
+       WID_WMM_STA_AC_PARAMS           = 0x4004,
+       WID_NETWORK_INFO                = 0x4005,
+       WID_STA_JOIN_INFO               = 0x4006,
+       WID_CONNECTED_STA_LIST          = 0x4007,
+
+       /* NMAC Binary WID list */
+       WID_11N_AUTORATE_TABLE          = 0x4080,
+
+       WID_SCAN_CHANNEL_LIST           = 0x4084,
+
+       WID_INFO_ELEMENT_PROBE          = 0x4085,
+       WID_INFO_ELEMENT_ASSOCIATE      = 0x4086,
+       WID_ADD_STA                     = 0X4087,
+       WID_REMOVE_STA                  = 0X4088,
+       WID_EDIT_STA                    = 0X4089,
+       WID_ADD_BEACON                  = 0x408a,
+
+       WID_SETUP_MULTICAST_FILTER      = 0x408b,
+
+       WID_ANTENNA_SELECTION           = 0x408c,
+       /* Miscellaneous WIDs */
+       WID_ALL                         = 0x7FFE,
+       WID_MAX                         = 0xFFFF
+};
+
+struct wilc;
+
+#endif