summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/microchip
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/net/wireless/microchip
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/net/wireless/microchip')
-rw-r--r--drivers/net/wireless/microchip/Kconfig15
-rw-r--r--drivers/net/wireless/microchip/Makefile2
-rw-r--r--drivers/net/wireless/microchip/wilc1000/Kconfig48
-rw-r--r--drivers/net/wireless/microchip/wilc1000/Makefile11
-rw-r--r--drivers/net/wireless/microchip/wilc1000/cfg80211.c1883
-rw-r--r--drivers/net/wireless/microchip/wilc1000/cfg80211.h26
-rw-r--r--drivers/net/wireless/microchip/wilc1000/fw.h143
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.c2026
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.h220
-rw-r--r--drivers/net/wireless/microchip/wilc1000/mon.c278
-rw-r--r--drivers/net/wireless/microchip/wilc1000/netdev.c1025
-rw-r--r--drivers/net/wireless/microchip/wilc1000/netdev.h305
-rw-r--r--drivers/net/wireless/microchip/wilc1000/sdio.c1076
-rw-r--r--drivers/net/wireless/microchip/wilc1000/spi.c1374
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.c1810
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.h475
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_cfg.c433
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_cfg.h55
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_if.h814
19 files changed, 12019 insertions, 0 deletions
diff --git a/drivers/net/wireless/microchip/Kconfig b/drivers/net/wireless/microchip/Kconfig
new file mode 100644
index 000000000..a6b46fb6b
--- /dev/null
+++ b/drivers/net/wireless/microchip/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+config WLAN_VENDOR_MICROCHIP
+ bool "Microchip devices"
+ default y
+ help
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_MICROCHIP
+source "drivers/net/wireless/microchip/wilc1000/Kconfig"
+endif # WLAN_VENDOR_MICROCHIP
diff --git a/drivers/net/wireless/microchip/Makefile b/drivers/net/wireless/microchip/Makefile
new file mode 100644
index 000000000..73b763c73
--- /dev/null
+++ b/drivers/net/wireless/microchip/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_WILC1000) += wilc1000/
diff --git a/drivers/net/wireless/microchip/wilc1000/Kconfig b/drivers/net/wireless/microchip/wilc1000/Kconfig
new file mode 100644
index 000000000..62cfcdc9a
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/Kconfig
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0
+config WILC1000
+ tristate
+ help
+ Add support for the Atmel WILC1000 802.11 b/g/n SoC.
+ This provides Wi-FI over an SDIO or SPI interface, and
+ is usually found in IoT devices.
+
+ This module only support IEEE 802.11n WiFi.
+
+config WILC1000_SDIO
+ tristate "Atmel WILC1000 SDIO (WiFi only)"
+ depends on CFG80211 && INET && MMC
+ select WILC1000
+ help
+ This module adds support for the SDIO interface of adapters using
+ WILC1000 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.
+
+config WILC1000_SPI
+ tristate "Atmel WILC1000 SPI (WiFi only)"
+ depends on CFG80211 && INET && SPI
+ select WILC1000
+ select CRC7
+ select CRC_ITU_T
+ help
+ This module adds support for the SPI interface of adapters using
+ WILC1000 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.
+
+config WILC1000_HW_OOB_INTR
+ bool "WILC1000 out of band interrupt"
+ depends on WILC1000_SDIO
+ help
+ This option enables out-of-band interrupt support for the WILC1000
+ 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 division interrupt.
diff --git a/drivers/net/wireless/microchip/wilc1000/Makefile b/drivers/net/wireless/microchip/wilc1000/Makefile
new file mode 100644
index 000000000..c3c9e34c1
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_WILC1000) += wilc1000.o
+
+wilc1000-objs := cfg80211.o netdev.o mon.o \
+ hif.o wlan_cfg.o wlan.o
+
+obj-$(CONFIG_WILC1000_SDIO) += wilc1000-sdio.o
+wilc1000-sdio-objs += sdio.o
+
+obj-$(CONFIG_WILC1000_SPI) += wilc1000-spi.o
+wilc1000-spi-objs += spi.o
diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.c b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
new file mode 100644
index 000000000..bb2748a19
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
@@ -0,0 +1,1883 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include "cfg80211.h"
+
+#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 WILC_INVALID_CHANNEL 0
+
+/* Operation at 2.4 GHz with channels 1-13 */
+#define WILC_WLAN_OPERATING_CLASS_2_4GHZ 0x51
+
+static const struct ieee80211_txrx_stypes
+ wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 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)
+ }
+};
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY
+};
+#endif
+
+struct wilc_p2p_mgmt_data {
+ int size;
+ u8 *buff;
+};
+
+struct wilc_p2p_pub_act_frame {
+ u8 category;
+ u8 action;
+ u8 oui[3];
+ u8 oui_type;
+ u8 oui_subtype;
+ u8 dialog_token;
+ u8 elem[];
+} __packed;
+
+struct wilc_vendor_specific_ie {
+ u8 tag_number;
+ u8 tag_len;
+ u8 oui[3];
+ u8 oui_type;
+ u8 attr[];
+} __packed;
+
+struct wilc_attr_entry {
+ u8 attr_type;
+ __le16 attr_len;
+ u8 val[];
+} __packed;
+
+struct wilc_attr_oper_ch {
+ u8 attr_type;
+ __le16 attr_len;
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u8 op_class;
+ u8 op_channel;
+} __packed;
+
+struct wilc_attr_ch_list {
+ u8 attr_type;
+ __le16 attr_len;
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u8 elem[];
+} __packed;
+
+struct wilc_ch_list_elem {
+ u8 op_class;
+ u8 no_of_channels;
+ u8 ch_list[];
+} __packed;
+
+static void cfg_scan_result(enum scan_event scan_event,
+ struct wilc_rcvd_net_info *info,
+ struct wilc_priv *priv)
+{
+ 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;
+
+ bss = cfg80211_inform_bss_frame(wiphy, channel, info->mgmt,
+ info->frame_len,
+ (s32)info->rssi * 100,
+ GFP_KERNEL);
+ cfg80211_put_bss(wiphy, bss);
+ } else if (scan_event == SCAN_EVENT_DONE) {
+ mutex_lock(&priv->scan_req_lock);
+
+ if (priv->scan_req) {
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+
+ cfg80211_scan_done(priv->scan_req, &info);
+ 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);
+
+ if (priv->scan_req) {
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+
+ cfg80211_scan_done(priv->scan_req, &info);
+ 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,
+ struct wilc_priv *priv)
+{
+ struct net_device *dev = priv->dev;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ struct wilc_conn_info *conn_info = &wfi_drv->conn_info;
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+
+ vif->connecting = false;
+
+ if (conn_disconn_evt == CONN_DISCONN_EVENT_CONN_RESP) {
+ u16 connect_status = conn_info->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)
+ wl->sta_ch = WILC_INVALID_CHANNEL;
+
+ netdev_err(dev, "Unspecified failure\n");
+ }
+
+ if (connect_status == WLAN_STATUS_SUCCESS)
+ memcpy(priv->associated_bss, conn_info->bssid,
+ ETH_ALEN);
+
+ cfg80211_ref_bss(wiphy, vif->bss);
+ cfg80211_connect_bss(dev, conn_info->bssid, vif->bss,
+ conn_info->req_ies,
+ conn_info->req_ies_len,
+ conn_info->resp_ies,
+ conn_info->resp_ies_len,
+ connect_status, GFP_KERNEL,
+ NL80211_TIMEOUT_UNSPECIFIED);
+
+ vif->bss = NULL;
+ } else if (conn_disconn_evt == CONN_DISCONN_EVENT_DISCONN_NOTIF) {
+ u16 reason = 0;
+
+ eth_zero_addr(priv->associated_bss);
+ wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+ if (vif->iftype != WILC_CLIENT_MODE) {
+ wl->sta_ch = WILC_INVALID_CHANNEL;
+ } else {
+ if (wfi_drv->ifc_up)
+ reason = 3;
+ else
+ reason = 1;
+ }
+
+ cfg80211_disconnected(dev, reason, NULL, 0, false, GFP_KERNEL);
+ }
+}
+
+struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl)
+{
+ struct wilc_vif *vif;
+
+ vif = list_first_or_null_rcu(&wl->vif_list, typeof(*vif), list);
+ if (!vif)
+ return ERR_PTR(-EINVAL);
+
+ return vif;
+}
+
+static int set_channel(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif;
+ u32 channelnum;
+ int result;
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_wl_to_vif(wl);
+ if (IS_ERR(vif)) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return PTR_ERR(vif);
+ }
+
+ channelnum = ieee80211_frequency_to_channel(chandef->chan->center_freq);
+
+ wl->op_ch = channelnum;
+ result = wilc_set_mac_chnl_num(vif, channelnum);
+ if (result)
+ netdev_err(vif->ndev, "Error in setting channel\n");
+
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return result;
+}
+
+static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
+{
+ struct wilc_vif *vif = netdev_priv(request->wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+ 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) {
+ netdev_err(vif->ndev, "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] = ieee80211_frequency_to_channel(freq);
+ }
+
+ 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, cfg_scan_result, request);
+
+ if (ret) {
+ priv->scan_req = NULL;
+ priv->cfg_scanning = false;
+ }
+
+ return ret;
+}
+
+static int connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc_priv *priv = &vif->priv;
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ int ret;
+ u32 i;
+ u8 security = WILC_FW_SEC_NO;
+ enum mfptype mfp_type = WILC_FW_MFP_NONE;
+ enum authtype auth_type = WILC_FW_AUTH_ANY;
+ u32 cipher_group;
+ struct cfg80211_bss *bss;
+ void *join_params;
+ u8 ch;
+
+ vif->connecting = true;
+
+ cipher_group = sme->crypto.cipher_group;
+ if (cipher_group != 0) {
+ 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;
+ netdev_err(dev, "%s: Unsupported cipher\n",
+ __func__);
+ 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;
+ }
+ }
+
+ switch (sme->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ auth_type = WILC_FW_AUTH_OPEN_SYSTEM;
+ break;
+
+ case NL80211_AUTHTYPE_SAE:
+ auth_type = WILC_FW_AUTH_SAE;
+ if (sme->ssid_len) {
+ memcpy(vif->auth.ssid.ssid, sme->ssid, sme->ssid_len);
+ vif->auth.ssid.ssid_len = sme->ssid_len;
+ }
+ vif->auth.key_mgmt_suite = sme->crypto.akm_suites[0];
+ ether_addr_copy(vif->auth.bssid, sme->bssid);
+ break;
+
+ default:
+ break;
+ }
+
+ if (sme->crypto.n_akm_suites) {
+ if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X)
+ auth_type = WILC_FW_AUTH_IEEE8021;
+ else if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_PSK_SHA256)
+ auth_type = WILC_FW_AUTH_OPEN_SYSTEM_SHA256;
+ else if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X_SHA256)
+ auth_type = WILC_FW_AUTH_IEE8021X_SHA256;
+ }
+
+ if (wfi_drv->usr_scan_req.scan_result) {
+ netdev_err(vif->ndev, "%s: Scan in progress\n", __func__);
+ ret = -EBUSY;
+ goto out_error;
+ }
+
+ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid,
+ sme->ssid_len, IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY(sme->privacy));
+ 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) {
+ netdev_err(dev, "%s: failed to construct join param\n",
+ __func__);
+ ret = -EINVAL;
+ goto out_put_bss;
+ }
+
+ ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+ 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.conn_result = cfg_connect_result;
+ wfi_drv->conn_info.priv = priv;
+ wfi_drv->conn_info.param = join_params;
+
+ if (sme->mfp == NL80211_MFP_OPTIONAL)
+ mfp_type = WILC_FW_MFP_OPTIONAL;
+ else if (sme->mfp == NL80211_MFP_REQUIRED)
+ mfp_type = WILC_FW_MFP_REQUIRED;
+
+ wfi_drv->conn_info.mfp_type = mfp_type;
+
+ ret = wilc_set_join_req(vif, bss->bssid, sme->ie, sme->ie_len);
+ if (ret) {
+ netdev_err(dev, "wilc_set_join_req(): Error\n");
+ 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);
+ vif->bss = bss;
+ 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_vif *vif = netdev_priv(dev);
+ struct wilc_priv *priv = &vif->priv;
+ struct wilc *wilc = vif->wilc;
+ int ret;
+
+ vif->connecting = false;
+
+ if (!wilc)
+ return -EIO;
+
+ if (wilc->close) {
+ /* already disconnected done */
+ cfg80211_disconnected(dev, 0, NULL, 0, true, GFP_KERNEL);
+ return 0;
+ }
+
+ if (vif->iftype != WILC_CLIENT_MODE)
+ wilc->sta_ch = WILC_INVALID_CHANNEL;
+ wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
+
+ priv->hif_drv->p2p_timeout = 0;
+
+ ret = wilc_disconnect(vif);
+ if (ret != 0) {
+ netdev_err(priv->dev, "Error in disconnecting\n");
+ ret = -EINVAL;
+ }
+
+ vif->bss = NULL;
+
+ return ret;
+}
+
+static int wilc_wfi_cfg_allocate_wpa_entry(struct wilc_priv *priv, u8 idx)
+{
+ if (!priv->wilc_gtk[idx]) {
+ priv->wilc_gtk[idx] = kzalloc_obj(*priv->wilc_gtk[idx]);
+ if (!priv->wilc_gtk[idx])
+ return -ENOMEM;
+ }
+
+ if (!priv->wilc_ptk[idx]) {
+ priv->wilc_ptk[idx] = kzalloc_obj(*priv->wilc_ptk[idx]);
+ if (!priv->wilc_ptk[idx])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int wilc_wfi_cfg_allocate_wpa_igtk_entry(struct wilc_priv *priv, u8 idx)
+{
+ idx -= 4;
+ if (!priv->wilc_igtk[idx]) {
+ priv->wilc_igtk[idx] = kzalloc_obj(*priv->wilc_igtk[idx]);
+ if (!priv->wilc_igtk[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 wireless_dev *wdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+
+{
+ int ret = 0, keylen = params->key_len;
+ const u8 *rx_mic = NULL;
+ const u8 *tx_mic = NULL;
+ u8 mode = WILC_FW_SEC_NO;
+ u8 op_mode;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+ struct wilc_wfi_key *key;
+
+ switch (params->cipher) {
+ 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 {
+ 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;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ ret = wilc_wfi_cfg_allocate_wpa_igtk_entry(priv, key_index);
+ if (ret)
+ return -ENOMEM;
+
+ key = priv->wilc_igtk[key_index - 4];
+ ret = wilc_wfi_cfg_copy_wpa_info(key, params);
+ if (ret)
+ return -ENOMEM;
+
+ if (priv->wdev.iftype == NL80211_IFTYPE_AP ||
+ priv->wdev.iftype == NL80211_IFTYPE_P2P_GO)
+ op_mode = WILC_AP_MODE;
+ else
+ op_mode = WILC_STATION_MODE;
+
+ ret = wilc_add_igtk(vif, params->key, keylen, params->seq,
+ params->seq_len, mac_addr, op_mode,
+ key_index);
+ break;
+
+ default:
+ netdev_err(wdev->netdev, "%s: Unsupported cipher\n", __func__);
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static int del_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
+ u8 key_index,
+ bool pairwise,
+ const u8 *mac_addr)
+{
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ if (!pairwise && (key_index == 4 || key_index == 5)) {
+ key_index -= 4;
+ if (priv->wilc_igtk[key_index]) {
+ kfree(priv->wilc_igtk[key_index]->key);
+ priv->wilc_igtk[key_index]->key = NULL;
+ kfree(priv->wilc_igtk[key_index]->seq);
+ priv->wilc_igtk[key_index]->seq = NULL;
+ kfree(priv->wilc_igtk[key_index]);
+ priv->wilc_igtk[key_index] = NULL;
+ }
+ } else {
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+static int get_key(struct wiphy *wiphy, struct wireless_dev *wdev, int link_id,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ void *cookie,
+ void (*callback)(void *cookie, struct key_params *))
+{
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+ struct key_params key_params;
+
+ if (!pairwise) {
+ if (key_index == 4 || key_index == 5) {
+ key_index -= 4;
+ key_params.key = priv->wilc_igtk[key_index]->key;
+ key_params.cipher = priv->wilc_igtk[key_index]->cipher;
+ key_params.key_len = priv->wilc_igtk[key_index]->key_len;
+ key_params.seq = priv->wilc_igtk[key_index]->seq;
+ key_params.seq_len = priv->wilc_igtk[key_index]->seq_len;
+ } else {
+ 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 {
+ 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;
+}
+
+/* wiphy_new_nm() will WARNON if not present */
+static int set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+ int link_id, u8 key_index, bool unicast,
+ bool multicast)
+{
+ return 0;
+}
+
+static int set_default_mgmt_key(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int link_id, u8 key_index)
+{
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+
+ return wilc_set_default_mgmt_key_index(vif, key_index);
+}
+
+static int get_station(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct net_device *dev = wdev->netdev;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc_priv *priv = &vif->priv;
+ 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) {
+ 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) {
+ netdev_err(dev, "sta required is not associated\n");
+ return -ENOENT;
+ }
+
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
+
+ wilc_get_inactive_time(vif, mac, &inactive_time);
+ sinfo->inactive_time = 1000 * inactive_time;
+ } else if (vif->iftype == WILC_STATION_MODE) {
+ struct rf_info stats;
+
+ if (!wilc->initialized)
+ return -EBUSY;
+
+ wilc_get_statistics(vif, &stats);
+
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL) |
+ BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
+ BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
+ BIT_ULL(NL80211_STA_INFO_TX_FAILED) |
+ BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+
+ 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);
+ }
+ return 0;
+}
+
+static int set_wiphy_params(struct wiphy *wiphy, int radio_idx, u32 changed)
+{
+ int ret = -EINVAL;
+ struct cfg_param_attr cfg_param_val;
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif;
+ struct wilc_priv *priv;
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_wl_to_vif(wl);
+ if (IS_ERR(vif))
+ goto out;
+
+ priv = &vif->priv;
+ cfg_param_val.flag = 0;
+
+ if (changed & WIPHY_PARAM_RETRY_SHORT) {
+ netdev_dbg(vif->ndev,
+ "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) {
+ netdev_dbg(vif->ndev,
+ "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) {
+ netdev_dbg(vif->ndev,
+ "Setting WIPHY_PARAM_FRAG_THRESHOLD %d\n",
+ wiphy->frag_threshold);
+ cfg_param_val.flag |= WILC_CFG_PARAM_FRAG_THRESHOLD;
+ cfg_param_val.frag_threshold = wiphy->frag_threshold;
+ } else {
+ netdev_err(vif->ndev,
+ "Fragmentation threshold out of range\n");
+ goto out;
+ }
+ }
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ if (wiphy->rts_threshold > 255) {
+ netdev_dbg(vif->ndev,
+ "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 {
+ netdev_err(vif->ndev, "RTS threshold out of range\n");
+ goto out;
+ }
+ }
+
+ ret = wilc_hif_set_cfg(vif, &cfg_param_val);
+ if (ret)
+ netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n");
+
+out:
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return ret;
+}
+
+static int set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct wilc_vif *vif = netdev_priv(netdev);
+ struct wilc_priv *priv = &vif->priv;
+ u32 i;
+ int ret = 0;
+ u8 flag = 0;
+
+ for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
+ if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
+ ETH_ALEN)) {
+ flag = PMKID_FOUND;
+ break;
+ }
+ }
+ if (i < WILC_MAX_NUM_PMKIDS) {
+ 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 {
+ netdev_err(netdev, "Invalid PMKID index\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ 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;
+ struct wilc_vif *vif = netdev_priv(netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
+ if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
+ ETH_ALEN)) {
+ memset(&priv->pmkid_list.pmkidlist[i], 0,
+ sizeof(struct wilc_pmkid));
+ break;
+ }
+ }
+
+ if (i == priv->pmkid_list.numpmkid)
+ return -EINVAL;
+
+ 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--;
+
+ return 0;
+}
+
+static int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+ struct wilc_vif *vif = netdev_priv(netdev);
+
+ memset(&vif->priv.pmkid_list, 0, sizeof(struct wilc_pmkid_attr));
+
+ return 0;
+}
+
+static inline void wilc_wfi_cfg_parse_ch_attr(u8 *buf, u32 len, u8 sta_ch)
+{
+ struct wilc_attr_entry *e;
+ struct wilc_attr_ch_list *ch_list;
+ struct wilc_attr_oper_ch *op_ch;
+ u32 index = 0;
+ u8 ch_list_idx = 0;
+ u8 op_ch_idx = 0;
+
+ if (sta_ch == WILC_INVALID_CHANNEL)
+ return;
+
+ while (index + sizeof(*e) <= len) {
+ u16 attr_size;
+
+ e = (struct wilc_attr_entry *)&buf[index];
+ attr_size = le16_to_cpu(e->attr_len);
+
+ if (index + sizeof(*e) + attr_size > len)
+ return;
+
+ if (e->attr_type == IEEE80211_P2P_ATTR_CHANNEL_LIST &&
+ attr_size >= (sizeof(struct wilc_attr_ch_list) - sizeof(*e)))
+ ch_list_idx = index;
+ else if (e->attr_type == IEEE80211_P2P_ATTR_OPER_CHANNEL &&
+ attr_size == (sizeof(struct wilc_attr_oper_ch) - sizeof(*e)))
+ op_ch_idx = index;
+
+ if (ch_list_idx && op_ch_idx)
+ break;
+
+ index += sizeof(*e) + attr_size;
+ }
+
+ if (ch_list_idx) {
+ u16 elem_size;
+
+ ch_list = (struct wilc_attr_ch_list *)&buf[ch_list_idx];
+ /* the number of bytes following the final 'elem' member */
+ elem_size = le16_to_cpu(ch_list->attr_len) -
+ (sizeof(*ch_list) - sizeof(struct wilc_attr_entry));
+ for (unsigned int i = 0; i < elem_size;) {
+ struct wilc_ch_list_elem *e;
+
+ e = (struct wilc_ch_list_elem *)(ch_list->elem + i);
+
+ i += sizeof(*e);
+ if (i > elem_size)
+ break;
+
+ i += e->no_of_channels;
+ if (i > elem_size)
+ break;
+
+ if (e->op_class == WILC_WLAN_OPERATING_CLASS_2_4GHZ) {
+ memset(e->ch_list, sta_ch, e->no_of_channels);
+ break;
+ }
+ }
+ }
+
+ if (op_ch_idx) {
+ op_ch = (struct wilc_attr_oper_ch *)&buf[op_ch_idx];
+ op_ch->op_class = WILC_WLAN_OPERATING_CLASS_2_4GHZ;
+ op_ch->op_channel = sta_ch;
+ }
+}
+
+bool wilc_wfi_mgmt_frame_rx(struct wilc_vif *vif, u8 *buff, u32 size)
+{
+ struct wilc *wl = vif->wilc;
+ struct wilc_priv *priv = &vif->priv;
+ int freq;
+
+ freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ);
+
+ return cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0);
+}
+
+void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size)
+{
+ struct wilc *wl = vif->wilc;
+ struct wilc_priv *priv = &vif->priv;
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ struct ieee80211_mgmt *mgmt;
+ struct wilc_vendor_specific_ie *p;
+ struct wilc_p2p_pub_act_frame *d;
+ int ie_offset = offsetof(struct ieee80211_mgmt, u) + sizeof(*d);
+ const u8 *vendor_ie;
+ u32 header, pkt_offset;
+ s32 freq;
+
+ header = get_unaligned_le32(buff - HOST_HDR_OFFSET);
+ pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header);
+
+ if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
+ bool ack = false;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)buff;
+
+ if (ieee80211_is_probe_resp(hdr->frame_control) ||
+ pkt_offset & IS_MGMT_STATUS_SUCCES)
+ ack = true;
+
+ cfg80211_mgmt_tx_status(&priv->wdev, priv->tx_cookie, buff,
+ size, ack, GFP_KERNEL);
+ return;
+ }
+
+ freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ);
+
+ mgmt = (struct ieee80211_mgmt *)buff;
+ if (!ieee80211_is_action(mgmt->frame_control))
+ goto out_rx_mgmt;
+
+ if (priv->cfg_scanning &&
+ time_after_eq(jiffies, (unsigned long)wfi_drv->p2p_timeout)) {
+ netdev_dbg(vif->ndev, "Receiving action wrong ch\n");
+ return;
+ }
+
+ if (!ieee80211_is_public_action((struct ieee80211_hdr *)buff, size))
+ goto out_rx_mgmt;
+
+ d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
+ if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP &&
+ d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP)
+ goto out_rx_mgmt;
+
+ vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
+ buff + ie_offset, size - ie_offset);
+ if (!vendor_ie)
+ goto out_rx_mgmt;
+
+ p = (struct wilc_vendor_specific_ie *)vendor_ie;
+ wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch);
+
+out_rx_mgmt:
+ 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(struct wilc_vif *vif, u64 cookie)
+{
+ struct wilc_priv *priv = &vif->priv;
+ struct wilc_wfi_p2p_listen_params *params = &priv->remain_on_ch_params;
+
+ if (cookie != params->listen_cookie)
+ return;
+
+ priv->p2p_listen_state = false;
+
+ cfg80211_remain_on_channel_expired(&priv->wdev, params->listen_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,
+ const u8 *rx_addr)
+{
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ if (wdev->iftype == NL80211_IFTYPE_AP) {
+ netdev_dbg(vif->ndev, "Required while in AP mode\n");
+ return ret;
+ }
+
+ ret = wilc_remain_on_channel(vif, cookie, chan->hw_value,
+ wilc_wfi_remain_on_channel_expired);
+ 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 = cookie;
+ priv->p2p_listen_state = true;
+ priv->remain_on_ch_params.listen_duration = duration;
+
+ cfg80211_ready_on_channel(wdev, cookie, chan, duration, GFP_KERNEL);
+ mod_timer(&vif->hif_drv->remain_on_ch_timer,
+ jiffies + msecs_to_jiffies(duration + 1000));
+
+ return ret;
+}
+
+static int cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ if (cookie != priv->remain_on_ch_params.listen_cookie)
+ return -ENOENT;
+
+ return wilc_listen_state_expired(vif, cookie);
+}
+
+static int mgmt_tx(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 cookie)
+{
+ struct ieee80211_channel *chan = params->chan;
+ unsigned int wait = params->wait;
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ const struct ieee80211_mgmt *mgmt;
+ struct wilc_p2p_mgmt_data *mgmt_tx;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+ struct host_if_drv *wfi_drv = priv->hif_drv;
+ struct wilc_vendor_specific_ie *p;
+ struct wilc_p2p_pub_act_frame *d;
+ int ie_offset = offsetof(struct ieee80211_mgmt, u) + sizeof(*d);
+ const u8 *vendor_ie;
+ int ret = 0;
+
+ priv->tx_cookie = cookie;
+ mgmt = (const struct ieee80211_mgmt *)buf;
+
+ if (!ieee80211_is_mgmt(mgmt->frame_control))
+ goto out;
+
+ mgmt_tx = kmalloc_obj(*mgmt_tx);
+ if (!mgmt_tx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mgmt_tx->buff = kmemdup(buf, len, GFP_KERNEL);
+ if (!mgmt_tx->buff) {
+ ret = -ENOMEM;
+ kfree(mgmt_tx);
+ goto out;
+ }
+
+ mgmt_tx->size = len;
+
+ if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+ wilc_set_mac_chnl_num(vif, chan->hw_value);
+ vif->wilc->op_ch = chan->hw_value;
+ goto out_txq_add_pkt;
+ }
+
+ if (!ieee80211_is_public_action((struct ieee80211_hdr *)buf, len)) {
+ if (chan)
+ wilc_set_mac_chnl_num(vif, chan->hw_value);
+ else
+ wilc_set_mac_chnl_num(vif, vif->wilc->op_ch);
+
+ goto out_set_timeout;
+ }
+
+ d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
+ if (d->oui_type != WLAN_OUI_TYPE_WFA_P2P ||
+ d->oui_subtype != GO_NEG_CONF) {
+ wilc_set_mac_chnl_num(vif, chan->hw_value);
+ vif->wilc->op_ch = chan->hw_value;
+ }
+
+ if (d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP)
+ goto out_set_timeout;
+
+ vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
+ mgmt_tx->buff + ie_offset,
+ len - ie_offset);
+ if (!vendor_ie)
+ goto out_set_timeout;
+
+ p = (struct wilc_vendor_specific_ie *)vendor_ie;
+ wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch);
+
+out_set_timeout:
+ wfi_drv->p2p_timeout = (jiffies + msecs_to_jiffies(wait));
+
+out_txq_add_pkt:
+
+ wilc_wlan_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_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+ 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(wdev,
+ params->listen_cookie,
+ params->listen_ch,
+ GFP_KERNEL);
+ }
+
+ return 0;
+}
+
+void wilc_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
+{
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ u32 presp_bit = BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
+ u32 action_bit = BIT(IEEE80211_STYPE_ACTION >> 4);
+ u32 pauth_bit = BIT(IEEE80211_STYPE_AUTH >> 4);
+
+ if (wl->initialized) {
+ bool prev = vif->mgmt_reg_stypes & presp_bit;
+ bool now = upd->interface_stypes & presp_bit;
+
+ if (now != prev)
+ wilc_frame_register(vif, IEEE80211_STYPE_PROBE_REQ, now);
+
+ prev = vif->mgmt_reg_stypes & action_bit;
+ now = upd->interface_stypes & action_bit;
+
+ if (now != prev)
+ wilc_frame_register(vif, IEEE80211_STYPE_ACTION, now);
+
+ prev = vif->mgmt_reg_stypes & pauth_bit;
+ now = upd->interface_stypes & pauth_bit;
+ if (now != prev)
+ wilc_frame_register(vif, IEEE80211_STYPE_AUTH, now);
+ }
+
+ vif->mgmt_reg_stypes =
+ upd->interface_stypes & (presp_bit | action_bit | pauth_bit);
+}
+
+static int external_auth(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_external_auth_params *auth)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+
+ if (auth->status == WLAN_STATUS_SUCCESS)
+ wilc_set_external_auth_param(vif, auth);
+
+ return 0;
+}
+
+static int set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ return 0;
+}
+
+static int dump_station(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ int ret;
+
+ if (idx != 0)
+ return -ENOENT;
+
+ ret = wilc_get_rssi(vif, &sinfo->signal);
+ if (ret)
+ return ret;
+
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
+ memcpy(mac, vif->priv.associated_bss, ETH_ALEN);
+ return 0;
+}
+
+static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ bool enabled, int timeout)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc_priv *priv = &vif->priv;
+
+ if (!priv->hif_drv)
+ return -EIO;
+
+ wilc_set_power_mgmt(vif, enabled, timeout);
+
+ return 0;
+}
+
+static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc_priv *priv = &vif->priv;
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ vif->connecting = false;
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev.iftype = type;
+ vif->monitor_flag = 0;
+ if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE)
+ wilc_wfi_deinit_mon_interface(wl, true);
+ vif->iftype = WILC_STATION_MODE;
+
+ if (wl->initialized)
+ wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+ WILC_STATION_MODE, vif->idx);
+
+ memset(priv->assoc_stainfo.sta_associated_bss, 0,
+ WILC_MAX_NUM_STA * ETH_ALEN);
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->connecting = false;
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev.iftype = type;
+ vif->monitor_flag = 0;
+ vif->iftype = WILC_CLIENT_MODE;
+
+ if (wl->initialized)
+ wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+ WILC_STATION_MODE, vif->idx);
+ break;
+
+ case NL80211_IFTYPE_AP:
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev.iftype = type;
+ vif->iftype = WILC_AP_MODE;
+
+ if (wl->initialized)
+ wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+ WILC_AP_MODE, vif->idx);
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ dev->ieee80211_ptr->iftype = type;
+ priv->wdev.iftype = type;
+ vif->iftype = WILC_GO_MODE;
+
+ if (wl->initialized)
+ wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+ WILC_AP_MODE, vif->idx);
+ break;
+
+ default:
+ netdev_err(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;
+
+ ret = set_channel(wiphy, dev, &settings->chandef);
+ if (ret != 0)
+ netdev_err(dev, "Error in setting channel\n");
+
+ wilc_wlan_set_bssid(dev, dev->dev_addr, WILC_AP_MODE);
+
+ 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_ap_update *params)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+
+ return wilc_add_beacon(vif, 0, 0, &params->beacon);
+}
+
+static int stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ unsigned int link_id)
+{
+ int ret;
+ struct wilc_vif *vif = netdev_priv(dev);
+
+ wilc_wlan_set_bssid(dev, NULL, WILC_AP_MODE);
+
+ ret = wilc_del_beacon(vif);
+
+ if (ret)
+ netdev_err(dev, "Host delete beacon fail\n");
+
+ return ret;
+}
+
+static int add_station(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *mac, struct station_parameters *params)
+{
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+ memcpy(priv->assoc_stainfo.sta_associated_bss[params->aid], mac,
+ ETH_ALEN);
+
+ ret = wilc_add_station(vif, mac, params);
+ if (ret)
+ netdev_err(wdev->netdev, "Host add station fail\n");
+ }
+
+ return ret;
+}
+
+static int del_station(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct station_del_parameters *params)
+{
+ const u8 *mac = params->mac;
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc_priv *priv = &vif->priv;
+ struct sta_info *info;
+
+ if (!(vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE))
+ return ret;
+
+ info = &priv->assoc_stainfo;
+
+ if (!mac)
+ ret = wilc_del_allstation(vif, info->sta_associated_bss);
+
+ ret = wilc_del_station(vif, mac);
+ if (ret)
+ netdev_err(wdev->netdev, "Host delete station fail\n");
+ return ret;
+}
+
+static int change_station(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *mac, struct station_parameters *params)
+{
+ int ret = 0;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
+ ret = wilc_edit_station(vif, mac, params);
+ if (ret)
+ netdev_err(wdev->netdev, "Host edit station fail\n");
+ }
+ return ret;
+}
+
+static struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type)
+{
+ struct wilc_vif *vif;
+
+ wilc_for_each_vif(wl, vif) {
+ if (vif->iftype == type)
+ return vif;
+ }
+
+ return NULL;
+}
+
+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)
+{
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif;
+ struct wireless_dev *wdev;
+ int iftype;
+
+ if (type == NL80211_IFTYPE_MONITOR) {
+ struct net_device *ndev;
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_vif_from_type(wl, WILC_AP_MODE);
+ if (!vif) {
+ vif = wilc_get_vif_from_type(wl, WILC_GO_MODE);
+ if (!vif) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ goto validate_interface;
+ }
+ }
+
+ if (vif->monitor_flag) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ goto validate_interface;
+ }
+
+ ndev = wilc_wfi_init_mon_interface(wl, name, vif->ndev);
+ if (ndev) {
+ vif->monitor_flag = 1;
+ } else {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ wdev = &vif->priv.wdev;
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return wdev;
+ }
+
+validate_interface:
+ mutex_lock(&wl->vif_mutex);
+ if (wl->vif_num == WILC_NUM_CONCURRENT_IFC) {
+ pr_err("Reached maximum number of interface\n");
+ mutex_unlock(&wl->vif_mutex);
+ return ERR_PTR(-EINVAL);
+ }
+ mutex_unlock(&wl->vif_mutex);
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ iftype = WILC_STATION_MODE;
+ break;
+ case NL80211_IFTYPE_AP:
+ iftype = WILC_AP_MODE;
+ break;
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ vif = wilc_netdev_ifc_init(wl, name, iftype, type, true);
+ if (IS_ERR(vif))
+ return ERR_CAST(vif);
+
+ return &vif->priv.wdev;
+}
+
+static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif;
+
+ if (wdev->iftype == NL80211_IFTYPE_AP ||
+ wdev->iftype == NL80211_IFTYPE_P2P_GO)
+ wilc_wfi_deinit_mon_interface(wl, true);
+ vif = netdev_priv(wdev->netdev);
+ cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL);
+ cfg80211_unregister_netdevice(vif->ndev);
+ vif->monitor_flag = 0;
+
+ mutex_lock(&wl->vif_mutex);
+ list_del_rcu(&vif->list);
+ wl->vif_num--;
+ mutex_unlock(&wl->vif_mutex);
+ synchronize_srcu(&wl->srcu);
+ return 0;
+}
+
+static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled)
+{
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif;
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_wl_to_vif(wl);
+ if (IS_ERR(vif)) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return;
+ }
+
+ netdev_info(vif->ndev, "cfg set wake up = %d\n", enabled);
+ wilc_set_wowlan_trigger(vif, enabled);
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+}
+
+static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int radio_idx, enum nl80211_tx_power_setting type,
+ int mbm)
+{
+ int ret;
+ int srcu_idx;
+ s32 tx_power = MBM_TO_DBM(mbm);
+ struct wilc *wl = wiphy_priv(wiphy);
+ struct wilc_vif *vif;
+
+ if (!wl->initialized)
+ return -EIO;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_wl_to_vif(wl);
+ if (IS_ERR(vif)) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return -EINVAL;
+ }
+
+ netdev_info(vif->ndev, "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)
+ netdev_err(vif->ndev, "Failed to set tx power\n");
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+
+ return ret;
+}
+
+static int get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int radio_idx, unsigned int link_id, int *dbm)
+{
+ int ret;
+ struct wilc_vif *vif = netdev_priv(wdev->netdev);
+ struct wilc *wl = vif->wilc;
+
+ /* If firmware is not started, return. */
+ if (!wl->initialized)
+ return -EIO;
+
+ ret = wilc_get_tx_power(vif, (u8 *)dbm);
+ if (ret)
+ netdev_err(vif->ndev, "Failed to get tx power\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,
+ .set_default_mgmt_key = set_default_mgmt_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,
+ .set_wiphy_params = set_wiphy_params,
+
+ .external_auth = external_auth,
+ .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,
+ .update_mgmt_frame_registrations = wilc_update_mgmt_frame_registrations,
+ .set_power_mgmt = set_power_mgmt,
+ .set_cqm_rssi_config = set_cqm_rssi_config,
+
+ .set_wakeup = wilc_set_wakeup,
+ .set_tx_power = set_tx_power,
+ .get_tx_power = get_tx_power,
+
+};
+
+static void wlan_init_locks(struct wilc *wl)
+{
+ mutex_init(&wl->hif_cs);
+ mutex_init(&wl->rxq_cs);
+ mutex_init(&wl->cfg_cmd_lock);
+ mutex_init(&wl->vif_mutex);
+ mutex_init(&wl->deinit_lock);
+
+ spin_lock_init(&wl->txq_spinlock);
+ mutex_init(&wl->txq_add_to_head_cs);
+
+ init_completion(&wl->txq_event);
+ init_completion(&wl->cfg_event);
+ init_completion(&wl->sync_event);
+ init_completion(&wl->txq_thread_started);
+ init_srcu_struct(&wl->srcu);
+}
+
+void wlan_deinit_locks(struct wilc *wilc)
+{
+ 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->vif_mutex);
+ mutex_destroy(&wilc->deinit_lock);
+ cleanup_srcu_struct(&wilc->srcu);
+}
+
+static struct wilc *wilc_create_wiphy(struct device *dev)
+{
+ struct wiphy *wiphy;
+ struct wilc *wl;
+
+ wiphy = wiphy_new(&wilc_cfg80211_ops, sizeof(*wl));
+ if (!wiphy)
+ return NULL;
+
+ wl = wiphy_priv(wiphy);
+
+ memcpy(wl->bitrates, wilc_bitrates, sizeof(wilc_bitrates));
+ memcpy(wl->channels, wilc_2ghz_channels, sizeof(wilc_2ghz_channels));
+ wl->band.bitrates = wl->bitrates;
+ wl->band.n_bitrates = ARRAY_SIZE(wl->bitrates);
+ wl->band.channels = wl->channels;
+ wl->band.n_channels = ARRAY_SIZE(wilc_2ghz_channels);
+
+ wl->band.ht_cap.ht_supported = 1;
+ wl->band.ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+ wl->band.ht_cap.mcs.rx_mask[0] = 0xff;
+ wl->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+ wl->band.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+
+ wiphy->bands[NL80211_BAND_2GHZ] = &wl->band;
+
+ wiphy->max_scan_ssids = WILC_MAX_NUM_PROBED_SSID;
+#ifdef CONFIG_PM
+ wiphy->wowlan = &wowlan_support;
+#endif
+ wiphy->max_num_pmkids = WILC_MAX_NUM_PMKIDS;
+ wiphy->max_scan_ie_len = 1000;
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ memcpy(wl->cipher_suites, wilc_cipher_suites,
+ sizeof(wilc_cipher_suites));
+ wiphy->cipher_suites = wl->cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(wilc_cipher_suites);
+ wiphy->mgmt_stypes = wilc_wfi_cfg80211_mgmt_types;
+
+ wiphy->max_remain_on_channel_duration = 500;
+ 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);
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ set_wiphy_dev(wiphy, dev);
+ wl->wiphy = wiphy;
+ return wl;
+}
+
+int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type,
+ const struct wilc_hif_func *ops)
+{
+ struct wilc *wl;
+ int ret, i;
+
+ wl = wilc_create_wiphy(dev);
+ if (!wl)
+ return -EINVAL;
+
+ wlan_init_locks(wl);
+
+ ret = wilc_wlan_cfg_init(wl);
+ if (ret)
+ goto free_wl;
+
+ *wilc = wl;
+ 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);
+ INIT_LIST_HEAD(&wl->vif_list);
+
+ wl->hif_workqueue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM,
+ wiphy_name(wl->wiphy));
+ if (!wl->hif_workqueue) {
+ ret = -ENOMEM;
+ goto free_cfg;
+ }
+
+ return 0;
+
+free_cfg:
+ wilc_wlan_cfg_deinit(wl);
+
+free_wl:
+ wlan_deinit_locks(wl);
+ wiphy_free(wl->wiphy);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wilc_cfg80211_init);
+
+int wilc_cfg80211_register(struct wilc *wilc)
+{
+ /* WPA3/SAE supported only on WILC1000 */
+ if (is_wilc1000(wilc->chipid))
+ wilc->wiphy->features |= NL80211_FEATURE_SAE;
+
+ return wiphy_register(wilc->wiphy);
+}
+EXPORT_SYMBOL_GPL(wilc_cfg80211_register);
+
+int wilc_init_host_int(struct net_device *net)
+{
+ int ret;
+ struct wilc_vif *vif = netdev_priv(net);
+ struct wilc_priv *priv = &vif->priv;
+
+ priv->p2p_listen_state = false;
+
+ mutex_init(&priv->scan_req_lock);
+ ret = wilc_init(net, &priv->hif_drv);
+ if (ret)
+ netdev_err(net, "Error while initializing hostinterface\n");
+
+ return ret;
+}
+
+void wilc_deinit_host_int(struct net_device *net)
+{
+ int ret;
+ struct wilc_vif *vif = netdev_priv(net);
+ struct wilc_priv *priv = &vif->priv;
+
+ priv->p2p_listen_state = false;
+
+ flush_workqueue(vif->wilc->hif_workqueue);
+ mutex_destroy(&priv->scan_req_lock);
+ ret = wilc_deinit(vif);
+
+ if (ret)
+ netdev_err(net, "Error while deinitializing host interface\n");
+}
+
diff --git a/drivers/net/wireless/microchip/wilc1000/cfg80211.h b/drivers/net/wireless/microchip/wilc1000/cfg80211.h
new file mode 100644
index 000000000..2dc9c1c42
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_CFG80211_H
+#define WILC_CFG80211_H
+#include "netdev.h"
+
+int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type,
+ const struct wilc_hif_func *ops);
+int wilc_cfg80211_register(struct wilc *wilc);
+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, bool rtnl_locked);
+struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
+ const char *name,
+ struct net_device *real_dev);
+void wilc_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd);
+struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl);
+void wlan_deinit_locks(struct wilc *wilc);
+#endif
diff --git a/drivers/net/wireless/microchip/wilc1000/fw.h b/drivers/net/wireless/microchip/wilc1000/fw.h
new file mode 100644
index 000000000..7a930e896
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/fw.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#ifndef WILC_FW_H
+#define WILC_FW_H
+
+#include <linux/ieee80211.h>
+
+#define WILC_MAX_NUM_STA 9
+#define WILC_MAX_RATES_SUPPORTED 12
+#define WILC_MAX_NUM_PMKIDS 16
+#define WILC_MAX_NUM_SCANNED_CH 14
+#define WILC_NVMEM_MAX_NUM_BANK 6
+#define WILC_NVMEM_BANK_BASE 0x30000000
+#define WILC_NVMEM_LOW_BANK_OFFSET 0x102c
+#define WILC_NVMEM_HIGH_BANK_OFFSET 0x1380
+#define WILC_NVMEM_IS_BANK_USED BIT(31)
+#define WILC_NVMEM_IS_BANK_INVALID BIT(30)
+
+struct wilc_assoc_resp {
+ __le16 capab_info;
+ __le16 status_code;
+ __le16 aid;
+} __packed;
+
+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 wilc_reg_frame {
+ u8 reg;
+ u8 reg_id;
+ __le16 frame_type;
+} __packed;
+
+struct wilc_drv_handler {
+ __le32 handler;
+ u8 mode;
+} __packed;
+
+struct wilc_sta_wpa_ptk {
+ u8 mac_addr[ETH_ALEN];
+ u8 key_len;
+ u8 key[];
+} __packed;
+
+struct wilc_ap_wpa_ptk {
+ u8 mac_addr[ETH_ALEN];
+ u8 index;
+ u8 key_len;
+ u8 key[];
+} __packed;
+
+struct wilc_wpa_igtk {
+ u8 index;
+ u8 pn_len;
+ u8 pn[6];
+ u8 key_len;
+ u8 key[];
+} __packed;
+
+struct wilc_gtk_key {
+ u8 mac_addr[ETH_ALEN];
+ u8 rsc[8];
+ u8 index;
+ u8 key_len;
+ u8 key[];
+} __packed;
+
+struct wilc_op_mode {
+ __le32 mode;
+} __packed;
+
+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;
+
+struct wilc_external_auth_param {
+ u8 action;
+ u8 bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_len;
+ __le32 key_mgmt_suites;
+ __le16 status;
+} __packed;
+
+static inline u32 get_bank_offset_from_bank_index(unsigned int i)
+{
+ return (((i) < 2) ? WILC_NVMEM_LOW_BANK_OFFSET + ((i) * 32) :
+ WILC_NVMEM_HIGH_BANK_OFFSET + ((i) - 2) * 16);
+}
+
+#endif
diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
new file mode 100644
index 000000000..60fe5f089
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/hif.c
@@ -0,0 +1,2026 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include "netdev.h"
+
+#define WILC_HIF_SCAN_TIMEOUT_MS 5000
+#define WILC_HIF_CONNECT_TIMEOUT_MS 9500
+
+#define WILC_FALSE_FRMWR_CHANNEL 100
+
+#define WILC_SCAN_WID_LIST_SIZE 6
+
+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 wilc_del_all_sta {
+ u8 assoc_sta;
+ u8 mac[WILC_MAX_NUM_STA][ETH_ALEN];
+};
+
+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 host_if_wowlan_trigger wow_trigger;
+};
+
+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;
+};
+
+/* '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_obj(*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;
+ struct wilc_vif *vif;
+
+ if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC)
+ return NULL;
+
+ wilc_for_each_vif(wilc, vif) {
+ if (vif->idx == index)
+ return vif;
+ }
+
+ return NULL;
+}
+
+static int handle_scan_done(struct wilc_vif *vif, enum scan_event evt)
+{
+ int 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;
+
+ if (evt == SCAN_EVENT_ABORTED) {
+ 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);
+ if (result) {
+ netdev_err(vif->ndev, "Failed to set abort running\n");
+ result = -EFAULT;
+ }
+ }
+
+ if (!hif_drv) {
+ netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__);
+ return result;
+ }
+
+ scan_req = &hif_drv->usr_scan_req;
+ if (scan_req->scan_result) {
+ scan_req->scan_result(evt, NULL, scan_req->priv);
+ scan_req->scan_result = NULL;
+ }
+
+ return result;
+}
+
+int wilc_scan(struct wilc_vif *vif, u8 scan_source,
+ u8 scan_type, u8 *ch_freq_list,
+ void (*scan_result_fn)(enum scan_event,
+ struct wilc_rcvd_net_info *,
+ struct wilc_priv *),
+ struct cfg80211_scan_request *request)
+{
+ int result = 0;
+ struct wid wid_list[WILC_SCAN_WID_LIST_SIZE];
+ u32 index = 0;
+ u32 i, scan_timeout;
+ u8 *buffer;
+ u32 valuesize = 0;
+ u8 *search_ssid_vals = NULL;
+ const u8 ch_list_len = request->n_channels;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (hif_drv->hif_state >= HOST_IF_SCANNING &&
+ hif_drv->hif_state < HOST_IF_CONNECTED) {
+ netdev_err(vif->ndev, "Already scan\n");
+ result = -EBUSY;
+ goto error;
+ }
+
+ if (vif->connecting) {
+ netdev_err(vif->ndev, "Don't do obss scan\n");
+ result = -EBUSY;
+ goto error;
+ }
+
+ 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;
+
+ 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 (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;
+ }
+
+ 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.priv = &vif->priv;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, index);
+ if (result) {
+ netdev_err(vif->ndev, "Failed to send scan parameters\n");
+ goto error;
+ }
+
+ hif_drv->scan_timer_vif = vif;
+ mod_timer(&hif_drv->scan_timer,
+ jiffies + msecs_to_jiffies(scan_timeout));
+
+error:
+
+ kfree(search_ssid_vals);
+
+ return result;
+}
+
+static int wilc_send_connect_wid(struct wilc_vif *vif)
+{
+ int result = 0;
+ struct wid wid_list[5];
+ 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 = conn_attr->param;
+
+
+ wid_list[wid_cnt].id = WID_SET_MFP;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ wid_list[wid_cnt].val = (s8 *)&conn_attr->mfp_type;
+ wid_cnt++;
+
+ 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++;
+
+ 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++;
+
+ 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++;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, wid_cnt);
+ if (result) {
+ netdev_err(vif->ndev, "failed to send config packet\n");
+ goto error;
+ } else {
+ if (conn_attr->auth_type == WILC_FW_AUTH_SAE)
+ hif_drv->hif_state = HOST_IF_EXTERNAL_AUTH;
+ else
+ 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) {
+ netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__);
+ goto out;
+ }
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ if (hif_drv->conn_info.conn_result) {
+ hif_drv->conn_info.conn_result(CONN_DISCONN_EVENT_CONN_RESP,
+ WILC_MAC_STATUS_DISCONNECTED,
+ hif_drv->conn_info.priv);
+
+ } else {
+ netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__);
+ }
+
+ wid.id = WID_DISCONNECT;
+ wid.type = WID_CHAR;
+ wid.val = (s8 *)&dummy_reason_code;
+ wid.size = sizeof(char);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result)
+ netdev_err(vif->ndev, "Failed to send disconnect\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);
+}
+
+struct wilc_join_bss_param *
+wilc_parse_join_bss_param(struct cfg80211_bss *bss,
+ struct cfg80211_crypto_settings *crypto)
+{
+ const u8 *ies_data, *tim_elm, *ssid_elm, *rates_ie, *supp_rates_ie;
+ const u8 *ht_ie, *wpa_ie, *wmm_ie, *rsn_ie;
+ struct ieee80211_p2p_noa_attr noa_attr;
+ const struct cfg80211_bss_ies *ies;
+ struct wilc_join_bss_param *param;
+ u8 rates_len = 0;
+ int ies_len;
+ u64 ies_tsf;
+ int ret;
+
+ param = kzalloc_obj(*param);
+ if (!param)
+ return NULL;
+
+ rcu_read_lock();
+ ies = rcu_dereference(bss->ies);
+ ies_data = kmemdup(ies->data, ies->len, GFP_ATOMIC);
+ if (!ies_data) {
+ rcu_read_unlock();
+ kfree(param);
+ return NULL;
+ }
+ ies_len = ies->len;
+ ies_tsf = ies->tsf;
+ rcu_read_unlock();
+
+ 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];
+ if (rates_len > WILC_MAX_RATES_SUPPORTED)
+ rates_len = WILC_MAX_RATES_SUPPORTED;
+ param->supp_rates[0] = rates_len;
+ memcpy(&param->supp_rates[1], rates_ie + 2, rates_len);
+ }
+
+ if (rates_len < WILC_MAX_RATES_SUPPORTED) {
+ supp_rates_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES,
+ ies_data, ies_len);
+ if (supp_rates_ie) {
+ u8 ext_rates = supp_rates_ie[1];
+
+ if (ext_rates > (WILC_MAX_RATES_SUPPORTED - rates_len))
+ param->supp_rates[0] = WILC_MAX_RATES_SUPPORTED;
+ else
+ param->supp_rates[0] += ext_rates;
+
+ 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 rsn_ie_len = sizeof(struct element) + rsn_ie[1];
+ int offset = 8;
+
+ param->mode_802_11i = 2;
+ param->rsn_found = true;
+
+ /* extract RSN capabilities */
+ if (offset < rsn_ie_len) {
+ /* skip over pairwise suites */
+ offset += (rsn_ie[offset] * 4) + 2;
+
+ if (offset < rsn_ie_len) {
+ /* skip over authentication suites */
+ offset += (rsn_ie[offset] * 4) + 2;
+
+ if (offset + 1 < rsn_ie_len)
+ 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;
+ }
+
+ kfree(ies_data);
+ 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;
+
+ 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;
+
+ 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->priv);
+
+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);
+ if (result) {
+ *rcvd_assoc_resp_info_len = 0;
+ netdev_err(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 wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer;
+
+ if (buffer_len < sizeof(*res)) {
+ ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -EINVAL;
+ }
+
+ 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);
+
+ if (assoc_resp_info_len != 0) {
+ s32 err = 0;
+
+ err = wilc_parse_assoc_resp_info(hif_drv->assoc_resp,
+ assoc_resp_info_len,
+ conn_info);
+ if (err)
+ netdev_err(vif->ndev,
+ "wilc_parse_assoc_resp_info() returned error %d\n",
+ err);
+ }
+ }
+
+ timer_delete(&hif_drv->connect_timer);
+ conn_info->conn_result(CONN_DISCONN_EVENT_CONN_RESP, mac_status,
+ hif_drv->conn_info.priv);
+
+ if (mac_status == WILC_MAC_STATUS_CONNECTED &&
+ conn_info->status == WLAN_STATUS_SUCCESS) {
+ ether_addr_copy(hif_drv->assoc_bssid, conn_info->bssid);
+ hif_drv->hif_state = HOST_IF_CONNECTED;
+ } else {
+ 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;
+}
+
+void wilc_handle_disconnect(struct wilc_vif *vif)
+{
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (hif_drv->usr_scan_req.scan_result) {
+ timer_delete(&hif_drv->scan_timer);
+ handle_scan_done(vif, SCAN_EVENT_ABORTED);
+ }
+
+ if (hif_drv->conn_info.conn_result)
+ hif_drv->conn_info.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF,
+ 0, hif_drv->conn_info.priv);
+
+ 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;
+ }
+
+ if (!hif_drv->conn_info.conn_result) {
+ netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__);
+ goto free_msg;
+ }
+
+
+ if (hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH) {
+ cfg80211_external_auth_request(vif->ndev, &vif->auth,
+ GFP_KERNEL);
+ hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP;
+ } else 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) {
+ wilc_handle_disconnect(vif);
+ } else if (hif_drv->usr_scan_req.scan_result) {
+ timer_delete(&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;
+
+ wid.id = WID_DISCONNECT;
+ wid.type = WID_CHAR;
+ wid.val = (s8 *)&dummy_reason_code;
+ wid.size = sizeof(char);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result) {
+ netdev_err(vif->ndev, "Failed to send disconnect\n");
+ return result;
+ }
+
+ scan_req = &hif_drv->usr_scan_req;
+ conn_info = &hif_drv->conn_info;
+
+ if (scan_req->scan_result) {
+ timer_delete(&hif_drv->scan_timer);
+ scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->priv);
+ scan_req->scan_result = NULL;
+ }
+
+ if (conn_info->conn_result) {
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
+ hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH)
+ timer_delete(&hif_drv->connect_timer);
+
+ conn_info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, 0,
+ conn_info->priv);
+ } else {
+ netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__);
+ }
+
+ 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;
+}
+
+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);
+ if (result) {
+ netdev_err(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)
+ wilc_enable_tcp_ack_filter(vif, true);
+ else if (stats->link_speed != DEFAULT_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(u8 *cur_byte, const u8 *mac,
+ struct station_parameters *params)
+{
+ ether_addr_copy(cur_byte, mac);
+ cur_byte += ETH_ALEN;
+
+ put_unaligned_le16(params->aid, cur_byte);
+ cur_byte += 2;
+
+ *cur_byte++ = params->link_sta_params.supported_rates_len;
+ if (params->link_sta_params.supported_rates_len > 0)
+ memcpy(cur_byte, params->link_sta_params.supported_rates,
+ params->link_sta_params.supported_rates_len);
+ cur_byte += params->link_sta_params.supported_rates_len;
+
+ if (params->link_sta_params.ht_capa) {
+ *cur_byte++ = true;
+ memcpy(cur_byte, params->link_sta_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;
+
+ if (hif_drv->usr_scan_req.scan_result)
+ return -EBUSY;
+
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP)
+ return -EBUSY;
+
+ if (vif->connecting)
+ return -EBUSY;
+
+ 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);
+ kfree(wid.val);
+ if (result)
+ return -EBUSY;
+
+ hif_drv->remain_on_ch.vif = hif_remain_ch->vif;
+ 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->remain_on_ch_timer_vif = vif;
+
+ return 0;
+}
+
+static int wilc_handle_roc_expired(struct wilc_vif *vif, u64 cookie)
+{
+ u8 remain_on_chan_flag;
+ struct wid wid;
+ int result;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (vif->priv.p2p_listen_state) {
+ 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)
+ return -ENOMEM;
+
+ 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);
+ kfree(wid.val);
+ if (result != 0) {
+ netdev_err(vif->ndev, "Failed to set remain channel\n");
+ return -EINVAL;
+ }
+
+ if (hif_drv->remain_on_ch.expired) {
+ hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.vif,
+ cookie);
+ }
+ } else {
+ netdev_dbg(vif->ndev, "Not in listen state\n");
+ }
+
+ return 0;
+}
+
+static void wilc_handle_listen_state_expired(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+
+ wilc_handle_roc_expired(msg->vif, msg->body.remain_on_ch.cookie);
+ kfree(msg);
+}
+
+static void listen_timer_cb(struct timer_list *t)
+{
+ struct host_if_drv *hif_drv = timer_container_of(hif_drv, t,
+ remain_on_ch_timer);
+ struct wilc_vif *vif = hif_drv->remain_on_ch_timer_vif;
+ int result;
+ struct host_if_msg *msg;
+
+ timer_delete(&vif->hif_drv->remain_on_ch_timer);
+
+ msg = wilc_alloc_work(vif, wilc_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) {
+ netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__);
+ 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;
+
+ 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);
+ if (result)
+ netdev_err(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, bool enabled)
+{
+ int ret;
+ struct wid wid;
+ u8 wowlan_trigger = 0;
+
+ if (enabled)
+ wowlan_trigger = 1;
+
+ wid.id = WID_WOWLAN_TRIGGER;
+ wid.type = WID_CHAR;
+ wid.val = &wowlan_trigger;
+ wid.size = sizeof(char);
+
+ ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (ret)
+ pr_err("Failed to send wowlan trigger config packet\n");
+}
+
+int wilc_set_external_auth_param(struct wilc_vif *vif,
+ struct cfg80211_external_auth_params *auth)
+{
+ int ret;
+ struct wid wid;
+ struct wilc_external_auth_param *param;
+
+ wid.id = WID_EXTERNAL_AUTH_PARAM;
+ wid.type = WID_BIN_DATA;
+ wid.size = sizeof(*param);
+ param = kzalloc_obj(*param);
+ if (!param)
+ return -EINVAL;
+
+ wid.val = (u8 *)param;
+ param->action = auth->action;
+ ether_addr_copy(param->bssid, auth->bssid);
+ memcpy(param->ssid, auth->ssid.ssid, auth->ssid.ssid_len);
+ param->ssid_len = auth->ssid.ssid_len;
+ ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+
+ kfree(param);
+ return ret;
+}
+
+static void handle_scan_timer(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+
+ handle_scan_done(msg->vif, SCAN_EVENT_ABORTED);
+ kfree(msg);
+}
+
+static void handle_scan_complete(struct work_struct *work)
+{
+ struct host_if_msg *msg = container_of(work, struct host_if_msg, work);
+
+ timer_delete(&msg->vif->hif_drv->scan_timer);
+
+ handle_scan_done(msg->vif, SCAN_EVENT_DONE);
+
+ kfree(msg);
+}
+
+static void timer_scan_cb(struct timer_list *t)
+{
+ struct host_if_drv *hif_drv = timer_container_of(hif_drv, t,
+ scan_timer);
+ 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);
+}
+
+static void timer_connect_cb(struct timer_list *t)
+{
+ struct host_if_drv *hif_drv = timer_container_of(hif_drv, t,
+ connect_timer);
+ 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);
+}
+
+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_flex(*key_buf, key, t_key_len);
+ if (!key_buf)
+ 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));
+ kfree(key_buf);
+ } else if (mode == WILC_STATION_MODE) {
+ struct wid wid;
+ struct wilc_sta_wpa_ptk *key_buf;
+
+ key_buf = kzalloc_flex(*key_buf, key, t_key_len);
+ if (!key_buf)
+ 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);
+ kfree(key_buf);
+ }
+
+ return result;
+}
+
+int wilc_add_igtk(struct wilc_vif *vif, const u8 *igtk, u8 igtk_key_len,
+ const u8 *pn, u8 pn_len, const u8 *mac_addr, u8 mode, u8 index)
+{
+ int result = 0;
+ u8 t_key_len = igtk_key_len;
+ struct wid wid;
+ struct wilc_wpa_igtk *key_buf;
+
+ key_buf = kzalloc_flex(*key_buf, key, t_key_len);
+ if (!key_buf)
+ return -ENOMEM;
+
+ key_buf->index = index;
+
+ memcpy(&key_buf->pn[0], pn, pn_len);
+ key_buf->pn_len = pn_len;
+
+ memcpy(&key_buf->key[0], igtk, igtk_key_len);
+ key_buf->key_len = t_key_len;
+
+ wid.id = WID_ADD_IGTK;
+ 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);
+ 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_flex(*gtk_key, key, t_key_len);
+ if (!gtk_key)
+ 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));
+ } 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);
+ }
+
+ 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);
+}
+
+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);
+ if (result)
+ netdev_err(vif->ndev, "Failed to get mac address\n");
+
+ return result;
+}
+
+int wilc_set_mac_address(struct wilc_vif *vif, const u8 *mac_addr)
+{
+ struct wid wid;
+ int result;
+
+ wid.id = WID_MAC_ADDR;
+ wid.type = WID_STR;
+ wid.size = ETH_ALEN;
+ wid.val = (u8 *)mac_addr;
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result)
+ netdev_err(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)
+ goto free_ies;
+
+ 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);
+ if (result)
+ netdev_err(vif->ndev, "Failed to set channel\n");
+
+ return result;
+}
+
+int wilc_set_operation_mode(struct wilc_vif *vif, int index, u8 mode,
+ u8 ifc_id)
+{
+ struct wid wid;
+ int result;
+ struct wilc_drv_handler drv;
+
+ wid.id = WID_SET_OPERATION_MODE;
+ 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);
+ if (result)
+ netdev_err(vif->ndev, "Failed to set driver handler\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)
+ return -ENOMEM;
+
+ ether_addr_copy(wid.val, mac);
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ kfree(wid.val);
+ if (result) {
+ netdev_err(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);
+ if (result)
+ netdev_err(vif->ndev, "Failed to get inactive time\n");
+
+ return result;
+}
+
+int wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level)
+{
+ struct wid wid;
+ int result;
+
+ if (!rssi_level) {
+ netdev_err(vif->ndev, "%s: RSSI level is NULL\n", __func__);
+ 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);
+ if (result)
+ netdev_err(vif->ndev, "Failed to get RSSI value\n");
+
+ return result;
+}
+
+static int wilc_get_stats_async(struct wilc_vif *vif, struct rf_info *stats)
+{
+ int result;
+ struct host_if_msg *msg;
+
+ 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) {
+ netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__);
+ 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);
+}
+
+static void get_periodic_rssi(struct timer_list *t)
+{
+ struct wilc_vif *vif = timer_container_of(vif, t, periodic_rssi);
+
+ if (!vif->hif_drv) {
+ netdev_err(vif->ndev, "%s: hif driver is NULL", __func__);
+ return;
+ }
+
+ if (vif->hif_drv->hif_state == HOST_IF_CONNECTED)
+ wilc_get_stats_async(vif, &vif->periodic_stat);
+
+ 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);
+
+ hif_drv = kzalloc_obj(*hif_drv);
+ if (!hif_drv)
+ return -ENOMEM;
+
+ *hif_drv_handler = hif_drv;
+
+ vif->hif_drv = hif_drv;
+
+ timer_setup(&vif->periodic_rssi, get_periodic_rssi, 0);
+ mod_timer(&vif->periodic_rssi, jiffies + msecs_to_jiffies(5000));
+
+ 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);
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ hif_drv->p2p_timeout = 0;
+
+ return 0;
+}
+
+int wilc_deinit(struct wilc_vif *vif)
+{
+ int result = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ netdev_err(vif->ndev, "%s: hif driver is NULL", __func__);
+ return -EFAULT;
+ }
+
+ mutex_lock(&vif->wilc->deinit_lock);
+
+ timer_shutdown_sync(&hif_drv->scan_timer);
+ timer_shutdown_sync(&hif_drv->connect_timer);
+ timer_delete_sync(&vif->periodic_rssi);
+ timer_shutdown_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.priv);
+ hif_drv->usr_scan_req.scan_result = NULL;
+ }
+
+ hif_drv->hif_state = HOST_IF_IDLE;
+
+ kfree(hif_drv);
+ vif->hif_drv = NULL;
+ mutex_unlock(&vif->wilc->deinit_lock);
+ return result;
+}
+
+void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+ struct host_if_drv *hif_drv;
+ struct host_if_msg *msg;
+ struct wilc_vif *vif;
+ int srcu_idx;
+ int result;
+ int id;
+
+ id = get_unaligned_le32(&buffer[length - 4]);
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ goto out;
+
+ hif_drv = vif->hif_drv;
+ if (!hif_drv) {
+ netdev_err(vif->ndev, "driver not init[%p]\n", hif_drv);
+ goto out;
+ }
+
+ msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false);
+ if (IS_ERR(msg))
+ goto out;
+
+ 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);
+ goto out;
+ }
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__);
+ kfree(msg->body.net_info.mgmt);
+ kfree(msg);
+ }
+out:
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+}
+
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+ struct host_if_drv *hif_drv;
+ struct host_if_msg *msg;
+ struct wilc_vif *vif;
+ int srcu_idx;
+ int result;
+ int id;
+
+ mutex_lock(&wilc->deinit_lock);
+
+ id = get_unaligned_le32(&buffer[length - 4]);
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ goto out;
+
+ hif_drv = vif->hif_drv;
+
+ if (!hif_drv) {
+ goto out;
+ }
+
+ if (!hif_drv->conn_info.conn_result) {
+ netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__);
+ goto out;
+ }
+
+ msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false);
+ if (IS_ERR(msg))
+ goto out;
+
+ msg->body.mac_info.status = buffer[7];
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__);
+ kfree(msg);
+ }
+out:
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+ mutex_unlock(&wilc->deinit_lock);
+}
+
+void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length)
+{
+ struct host_if_drv *hif_drv;
+ struct wilc_vif *vif;
+ int srcu_idx;
+ int result;
+ int id;
+
+ id = get_unaligned_le32(&buffer[length - 4]);
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ goto out;
+
+ hif_drv = vif->hif_drv;
+ if (!hif_drv) {
+ goto out;
+ }
+
+ 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))
+ goto out;
+
+ result = wilc_enqueue_work(msg);
+ if (result) {
+ netdev_err(vif->ndev, "%s: enqueue work failed\n",
+ __func__);
+ kfree(msg);
+ }
+ }
+out:
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+}
+
+int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan,
+ void (*expired)(struct wilc_vif *, u64))
+{
+ struct wilc_remain_ch roc;
+ int result;
+
+ roc.ch = chan;
+ roc.expired = expired;
+ roc.vif = vif;
+ roc.cookie = cookie;
+ result = handle_remain_on_chan(vif, &roc);
+ if (result)
+ netdev_err(vif->ndev, "%s: failed to set remain on channel\n",
+ __func__);
+
+ return result;
+}
+
+int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie)
+{
+ if (!vif->hif_drv) {
+ netdev_err(vif->ndev, "%s: hif driver is NULL", __func__);
+ return -EFAULT;
+ }
+
+ timer_delete(&vif->hif_drv->remain_on_ch_timer);
+
+ return wilc_handle_roc_expired(vif, cookie);
+}
+
+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));
+
+ if (reg)
+ reg_frame.reg = 1;
+
+ switch (frame_type) {
+ case IEEE80211_STYPE_ACTION:
+ reg_frame.reg_id = WILC_FW_ACTION_FRM_IDX;
+ break;
+
+ case IEEE80211_STYPE_PROBE_REQ:
+ reg_frame.reg_id = WILC_FW_PROBE_REQ_IDX;
+ break;
+
+ case IEEE80211_STYPE_AUTH:
+ reg_frame.reg_id = WILC_FW_AUTH_REQ_IDX;
+ break;
+
+ default:
+ break;
+ }
+ reg_frame.frame_type = cpu_to_le16(frame_type);
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result)
+ netdev_err(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;
+
+ 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)
+ 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);
+ if (result)
+ netdev_err(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;
+
+ 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);
+ if (result)
+ netdev_err(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;
+
+ wid.id = WID_ADD_STA;
+ wid.type = WID_BIN;
+ wid.size = WILC_ADD_STA_LENGTH +
+ params->link_sta_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(cur_byte, mac, params);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result != 0)
+ netdev_err(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;
+
+ wid.id = WID_REMOVE_STA;
+ wid.type = WID_BIN;
+ wid.size = ETH_ALEN;
+ wid.val = kzalloc(wid.size, GFP_KERNEL);
+ if (!wid.val)
+ 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);
+ if (result)
+ netdev_err(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;
+
+ memset(&del_sta, 0x0, sizeof(del_sta));
+ for (i = 0; i < WILC_MAX_NUM_STA; i++) {
+ if (!is_zero_ether_addr(mac_addr[i])) {
+ assoc_sta++;
+ ether_addr_copy(del_sta.mac[i], mac_addr[i]);
+ }
+ }
+
+ if (!assoc_sta)
+ 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);
+ if (result)
+ netdev_err(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;
+
+ wid.id = WID_EDIT_STA;
+ wid.type = WID_BIN;
+ wid.size = WILC_ADD_STA_LENGTH +
+ params->link_sta_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(cur_byte, mac, params);
+
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result)
+ netdev_err(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 wilc *wilc = vif->wilc;
+ struct wid wid;
+ int result;
+ s8 power_mode;
+
+ 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);
+ if (result)
+ netdev_err(vif->ndev, "Failed to send power management\n");
+ else
+ wilc->power_save_mode = enabled;
+
+ 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;
+
+ 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) {
+ netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__);
+ kfree(msg);
+ }
+ return result;
+}
+
+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);
+}
+
+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);
+}
+
+int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index)
+{
+ struct wid wid;
+ int result;
+
+ wid.id = WID_DEFAULT_MGMT_KEY_ID;
+ wid.type = WID_CHAR;
+ wid.size = sizeof(char);
+ wid.val = &index;
+ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
+ if (result)
+ netdev_err(vif->ndev,
+ "Failed to send default mgmt key index\n");
+
+ return result;
+}
diff --git a/drivers/net/wireless/microchip/wilc1000/hif.h b/drivers/net/wireless/microchip/wilc1000/hif.h
new file mode 100644
index 000000000..96eeaf31d
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/hif.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries
+ * All rights reserved.
+ */
+
+#ifndef WILC_HIF_H
+#define WILC_HIF_H
+#include <linux/ieee80211.h>
+#include "wlan_if.h"
+
+enum {
+ WILC_IDLE_MODE = 0x0,
+ WILC_AP_MODE = 0x1,
+ WILC_STATION_MODE = 0x2,
+ WILC_GO_MODE = 0x3,
+ WILC_CLIENT_MODE = 0x4
+};
+
+#define WILC_MAX_NUM_PROBED_SSID 10
+
+#define WILC_TX_MIC_KEY_LEN 8
+#define WILC_RX_MIC_KEY_LEN 8
+
+#define WILC_ADD_STA_LENGTH 40
+#define WILC_NUM_CONCURRENT_IFC 2
+
+enum {
+ WILC_SET_CFG = 0,
+ WILC_GET_CFG
+};
+
+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_EXTERNAL_AUTH = 6,
+ HOST_IF_FORCE_32BIT = 0xFFFFFFFF
+};
+
+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 {
+ CONN_DISCONN_EVENT_CONN_RESP = 0,
+ CONN_DISCONN_EVENT_DISCONN_NOTIF = 1,
+ CONN_DISCONN_EVENT_FORCE_32BIT = 0xFFFFFFFF
+};
+
+enum {
+ WILC_HIF_SDIO = 0,
+ WILC_HIF_SPI = BIT(0)
+};
+
+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;
+};
+
+struct wilc_priv;
+struct wilc_user_scan_req {
+ void (*scan_result)(enum scan_event evt,
+ struct wilc_rcvd_net_info *info,
+ struct wilc_priv *priv);
+ struct wilc_priv *priv;
+ u32 ch_cnt;
+};
+
+struct wilc_join_bss_param;
+struct wilc_conn_info {
+ u8 bssid[ETH_ALEN];
+ u8 security;
+ enum authtype auth_type;
+ enum mfptype mfp_type;
+ 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,
+ struct wilc_priv *priv);
+ struct wilc_priv *priv;
+ struct wilc_join_bss_param *param;
+};
+
+struct wilc_vif;
+struct wilc_remain_ch {
+ u16 ch;
+ void (*expired)(struct wilc_vif *vif, u64 cookie);
+ struct wilc_vif *vif;
+ u64 cookie;
+};
+
+struct wilc;
+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 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;
+ u8 assoc_resp[WILC_MAX_ASSOC_RESP_FRAME_SIZE];
+};
+
+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 wilc_add_igtk(struct wilc_vif *vif, const u8 *igtk, u8 igtk_key_len,
+ const u8 *pn, u8 pn_len, const u8 *mac_addr, u8 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, const 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,
+ void (*scan_result_fn)(enum scan_event,
+ struct wilc_rcvd_net_info *,
+ struct wilc_priv *),
+ 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, u16 chan,
+ void (*expired)(struct wilc_vif *, u64));
+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_operation_mode(struct wilc_vif *vif, int index, u8 mode,
+ u8 ifc_id);
+int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats);
+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);
+void wilc_set_wowlan_trigger(struct wilc_vif *vif, bool enabled);
+int wilc_set_external_auth_param(struct wilc_vif *vif,
+ struct cfg80211_external_auth_params *param);
+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);
+struct wilc_join_bss_param *
+wilc_parse_join_bss_param(struct cfg80211_bss *bss,
+ struct cfg80211_crypto_settings *crypto);
+int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index);
+void wilc_handle_disconnect(struct wilc_vif *vif);
+
+#endif
diff --git a/drivers/net/wireless/microchip/wilc1000/mon.c b/drivers/net/wireless/microchip/wilc1000/mon.c
new file mode 100644
index 000000000..9b8c083b4
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/mon.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include "cfg80211.h"
+
+struct wilc_wfi_radiotap_hdr {
+ struct ieee80211_radiotap_header_fixed hdr;
+ u8 rate;
+} __packed;
+
+struct wilc_wfi_radiotap_cb_hdr {
+ struct ieee80211_radiotap_header_fixed 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_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size)
+{
+ u32 header, pkt_offset;
+ struct sk_buff *skb = NULL;
+ struct wilc_wfi_radiotap_hdr *hdr;
+ struct wilc_wfi_radiotap_cb_hdr *cb_hdr;
+
+ if (!mon_dev)
+ return;
+
+ if (!netif_running(mon_dev))
+ return;
+
+ /* Get WILC header */
+ header = get_unaligned_le32(buff - HOST_HDR_OFFSET);
+ /*
+ * The packet offset field contain info about what type of management
+ * the frame we are dealing with and ack status
+ */
+ pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header);
+
+ if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
+ /* hostapd callback mgmt frame */
+
+ skb = dev_alloc_skb(size + sizeof(*cb_hdr));
+ if (!skb)
+ return;
+
+ skb_put_data(skb, buff, size);
+
+ cb_hdr = skb_push(skb, sizeof(*cb_hdr));
+ 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)
+ return;
+
+ skb_put_data(skb, buff, size);
+ hdr = skb_push(skb, sizeof(*hdr));
+ memset(hdr, 0, sizeof(struct wilc_wfi_radiotap_hdr));
+ hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */
+ hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+ hdr->hdr.it_present = cpu_to_le32
+ (1 << IEEE80211_RADIOTAP_RATE);
+ 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;
+ /*
+ * 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)
+ return -EFAULT;
+
+ netif_stop_queue(dev);
+ mgmt_tx = kmalloc_obj(*mgmt_tx, GFP_ATOMIC);
+ if (!mgmt_tx)
+ return -ENOMEM;
+
+ mgmt_tx->buff = kmemdup(buf, len, GFP_ATOMIC);
+ if (!mgmt_tx->buff) {
+ kfree(mgmt_tx);
+ return -ENOMEM;
+ }
+
+ mgmt_tx->size = len;
+
+ wilc_wlan_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)
+{
+ struct ieee80211_radiotap_header_fixed *rtap_hdr;
+ struct ieee80211_hdr_3addr *hdr;
+ unsigned int hdr_len;
+ u32 rtap_len, ret = 0;
+ struct wilc_wfi_mon_priv *mon_priv;
+ struct sk_buff *skb2;
+ struct wilc_wfi_radiotap_cb_hdr *cb_hdr;
+ u8 srcadd[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+
+ mon_priv = netdev_priv(dev);
+ if (!mon_priv)
+ return -EFAULT;
+
+ if (skb->len < sizeof(*rtap_hdr))
+ goto drop;
+
+ rtap_hdr = (void *)skb->data;
+ if (rtap_hdr->it_version)
+ goto drop;
+
+ rtap_len = ieee80211_get_radiotap_len(skb->data);
+ if (rtap_len < sizeof(*rtap_hdr) || skb->len < rtap_len)
+ goto drop;
+
+ skb_pull(skb, rtap_len);
+ hdr_len = ieee80211_get_hdrlen_from_skb(skb);
+ if (hdr_len < sizeof(*hdr))
+ goto drop;
+
+ hdr = (void *)skb->data;
+
+ if (ieee80211_is_deauth(hdr->frame_control) &&
+ is_broadcast_ether_addr(hdr->addr1)) {
+ skb2 = dev_alloc_skb(skb->len + sizeof(*cb_hdr));
+ if (!skb2)
+ return -ENOMEM;
+
+ skb_put_data(skb2, skb->data, skb->len);
+
+ cb_hdr = skb_push(skb2, sizeof(*cb_hdr));
+ memset(cb_hdr, 0, sizeof(struct wilc_wfi_radiotap_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;
+ cb_hdr->tx_flags = 0x0004;
+
+ skb2->dev = dev;
+ skb_reset_mac_header(skb2);
+ skb2->ip_summed = CHECKSUM_UNNECESSARY;
+ skb2->pkt_type = PACKET_OTHERHOST;
+ skb2->protocol = htons(ETH_P_802_2);
+ memset(skb2->cb, 0, sizeof(skb2->cb));
+
+ netif_rx(skb2);
+
+ return 0;
+ }
+ skb->dev = mon_priv->real_ndev;
+
+ ether_addr_copy(srcadd, hdr->addr2);
+ ether_addr_copy(bssid, hdr->addr3);
+ /*
+ * 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)
+ netdev_err(dev, "fail to mgmt tx\n");
+ dev_kfree_skb(skb);
+ } else {
+ ret = wilc_mac_xmit(skb, mon_priv->real_ndev);
+ }
+
+ return ret;
+
+drop:
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+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)
+ return NULL;
+
+ wl->monitor_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ strscpy(wl->monitor_dev->name, name, IFNAMSIZ);
+ wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops;
+ wl->monitor_dev->needs_free_netdev = true;
+
+ if (register_netdevice(wl->monitor_dev)) {
+ netdev_err(real_dev, "register_netdevice failed\n");
+ free_netdev(wl->monitor_dev);
+ return NULL;
+ }
+ priv = netdev_priv(wl->monitor_dev);
+
+ priv->real_ndev = real_dev;
+
+ return wl->monitor_dev;
+}
+
+void wilc_wfi_deinit_mon_interface(struct wilc *wl, bool rtnl_locked)
+{
+ if (!wl->monitor_dev)
+ return;
+
+ if (rtnl_locked)
+ unregister_netdevice(wl->monitor_dev);
+ else
+ unregister_netdev(wl->monitor_dev);
+ wl->monitor_dev = NULL;
+}
diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c
new file mode 100644
index 000000000..956cb578b
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/netdev.c
@@ -0,0 +1,1025 @@
+// 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/netdevice.h>
+#include <linux/inetdevice.h>
+
+#include "cfg80211.h"
+#include "wlan_cfg.h"
+
+#define WILC_MULTICAST_TABLE_SIZE 8
+#define WILC_MAX_FW_VERSION_STR_SIZE 50
+
+/* latest API version supported */
+#define WILC1000_API_VER 1
+
+#define WILC1000_FW_PREFIX "atmel/wilc1000_wifi_firmware-"
+#define __WILC1000_FW(api) WILC1000_FW_PREFIX #api ".bin"
+#define WILC1000_FW(api) __WILC1000_FW(api)
+
+#define WILC3000_API_VER 1
+
+#define WILC3000_FW_PREFIX "atmel/wilc3000_wifi_firmware-"
+#define __WILC3000_FW(api) WILC3000_FW_PREFIX #api ".bin"
+#define WILC3000_FW(api) __WILC3000_FW(api)
+
+static irqreturn_t isr_uh_routine(int irq, void *user_data)
+{
+ struct wilc *wilc = user_data;
+
+ if (wilc->close) {
+ pr_err("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 = userdata;
+
+ if (wilc->close) {
+ pr_err("Can't handle BH interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ wilc_handle_isr(wilc);
+
+ return IRQ_HANDLED;
+}
+
+static int init_irq(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+ int ret;
+
+ ret = request_threaded_irq(wl->dev_irq_num, isr_uh_routine,
+ isr_bh_routine,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev->name, wl);
+ if (ret) {
+ netdev_err(dev, "Failed to request IRQ [%d]\n", ret);
+ return ret;
+ }
+ netdev_dbg(dev, "IRQ request succeeded IRQ-NUM= %d\n", wl->dev_irq_num);
+
+ return 0;
+}
+
+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)
+ free_irq(wilc->dev_irq_num, wilc);
+}
+
+void wilc_mac_indicate(struct wilc *wilc)
+{
+ s8 status;
+
+ wilc_wlan_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;
+ }
+}
+
+static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
+{
+ struct net_device *ndev = NULL;
+ struct wilc_vif *vif;
+ struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header;
+
+ wilc_for_each_vif(wilc, vif) {
+ if (vif->iftype == WILC_STATION_MODE)
+ if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) {
+ ndev = vif->ndev;
+ goto out;
+ }
+ if (vif->iftype == WILC_AP_MODE)
+ if (ether_addr_equal_unaligned(h->addr1, vif->bssid)) {
+ ndev = vif->ndev;
+ goto out;
+ }
+ }
+out:
+ return ndev;
+}
+
+void wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid,
+ u8 mode)
+{
+ struct wilc_vif *vif = netdev_priv(wilc_netdev);
+
+ if (bssid)
+ ether_addr_copy(vif->bssid, bssid);
+ else
+ eth_zero_addr(vif->bssid);
+
+ vif->iftype = mode;
+}
+
+int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
+{
+ int srcu_idx;
+ u8 ret_val = 0;
+ struct wilc_vif *vif;
+
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ wilc_for_each_vif(wilc, vif) {
+ if (!is_zero_ether_addr(vif->bssid))
+ ret_val++;
+ }
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+ return ret_val;
+}
+
+static void wilc_wake_tx_queues(struct wilc *wl)
+{
+ int srcu_idx;
+ struct wilc_vif *ifc;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ wilc_for_each_vif(wl, ifc) {
+ if (ifc->mac_opened && netif_queue_stopped(ifc->ndev))
+ netif_wake_queue(ifc->ndev);
+ }
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+}
+
+static int wilc_txq_task(void *vp)
+{
+ int ret;
+ u32 txq_count;
+ struct wilc *wl = vp;
+
+ complete(&wl->txq_thread_started);
+ while (1) {
+ if (wait_for_completion_interruptible(&wl->txq_event))
+ continue;
+ if (wl->close) {
+ complete(&wl->txq_thread_started);
+
+ while (!kthread_should_stop())
+ schedule();
+ break;
+ }
+ do {
+ ret = wilc_wlan_handle_txq(wl, &txq_count);
+ if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) {
+ wilc_wake_tx_queues(wl);
+ }
+ if (ret != WILC_VMM_ENTRY_FULL_RETRY)
+ break;
+ /* Back off TX task from sending packets for some time.
+ * msleep_interruptible will allow RX task to run and
+ * free buffers. TX task will be in TASK_INTERRUPTIBLE
+ * state which will put the thread back to CPU running
+ * queue when it's signaled even if the timeout isn't
+ * elapsed. This gives faster chance for reserved SK
+ * buffers to be free.
+ */
+ msleep_interruptible(TX_BACKOFF_WEIGHT_MS);
+ } while (!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;
+ const struct firmware *wilc_fw;
+ char *firmware;
+ int ret;
+
+ if (is_wilc1000(wilc->chipid))
+ firmware = WILC1000_FW(WILC1000_API_VER);
+ else if (is_wilc3000(wilc->chipid))
+ firmware = WILC3000_FW(WILC3000_API_VER);
+ else
+ return -EINVAL;
+
+ netdev_info(dev, "WILC%d loading firmware [%s]\n",
+ is_wilc1000(wilc->chipid) ? 1000 : 3000,
+ firmware);
+
+ ret = request_firmware(&wilc_fw, firmware, wilc->dev);
+ if (ret != 0) {
+ netdev_err(dev, "%s - firmware not available\n", firmware);
+ return -EINVAL;
+ }
+ wilc->firmware = wilc_fw;
+
+ return 0;
+}
+
+static int wilc_start_firmware(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+ int ret = 0;
+
+ ret = wilc_wlan_start(wilc);
+ if (ret)
+ return ret;
+
+ if (!wait_for_completion_timeout(&wilc->sync_event,
+ msecs_to_jiffies(5000)))
+ return -ETIME;
+
+ 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) {
+ netdev_err(dev, "Firmware buffer is NULL\n");
+ return -ENOBUFS;
+ }
+
+ ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data,
+ wilc->firmware->size);
+ if (ret)
+ return ret;
+
+ release_firmware(wilc->firmware);
+ wilc->firmware = NULL;
+
+ netdev_dbg(dev, "Download Succeeded\n");
+
+ return 0;
+}
+
+static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif)
+{
+ struct wilc_priv *priv = &vif->priv;
+ struct host_if_drv *hif_drv;
+ u8 b;
+ u16 hw;
+ u32 w;
+
+ netdev_dbg(dev, "Start configuring Firmware\n");
+ hif_drv = (struct host_if_drv *)priv->hif_drv;
+ netdev_dbg(dev, "Host = %p\n", hif_drv);
+
+ w = vif->iftype;
+ cpu_to_le32s(&w);
+ if (!wilc_wlan_cfg_set(vif, 1, WID_SET_OPERATION_MODE, (u8 *)&w, 4,
+ 0, 0))
+ goto fail;
+
+ b = WILC_FW_BSS_TYPE_INFRA;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_BSS_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_TX_RATE_AUTO;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_CURRENT_TX_RATE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_OPER_MODE_G_MIXED_11B_2;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_PREAMBLE_AUTO;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_11N_PROT_AUTO;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_PROT_MECH, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_ACTIVE_SCAN;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_SCAN_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_SITE_SURVEY_OFF;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_SITE_SURVEY, &b, 1, 0, 0))
+ goto fail;
+
+ hw = 0xffff;
+ cpu_to_le16s(&hw);
+ if (!wilc_wlan_cfg_set(vif, 0, WID_RTS_THRESHOLD, (u8 *)&hw, 2, 0, 0))
+ goto fail;
+
+ hw = 2346;
+ cpu_to_le16s(&hw);
+ if (!wilc_wlan_cfg_set(vif, 0, WID_FRAG_THRESHOLD, (u8 *)&hw, 2, 0, 0))
+ goto fail;
+
+ b = 0;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_BCAST_SSID, &b, 1, 0, 0))
+ goto fail;
+
+ b = 1;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_QOS_ENABLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_NO_POWERSAVE;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_POWER_MANAGEMENT, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_SEC_NO;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11I_MODE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_AUTH_OPEN_SYSTEM;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_AUTH_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 3;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_LISTEN_INTERVAL, &b, 1, 0, 0))
+ goto fail;
+
+ b = 3;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_DTIM_PERIOD, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_ACK_POLICY_NORMAL;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_ACK_POLICY, &b, 1, 0, 0))
+ goto fail;
+
+ b = 0;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_USER_CONTROL_ON_TX_POWER, &b, 1,
+ 0, 0))
+ goto fail;
+
+ b = 48;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11A, &b, 1, 0, 0))
+ goto fail;
+
+ b = 28;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_TX_POWER_LEVEL_11B, &b, 1, 0, 0))
+ goto fail;
+
+ hw = 100;
+ cpu_to_le16s(&hw);
+ if (!wilc_wlan_cfg_set(vif, 0, WID_BEACON_INTERVAL, (u8 *)&hw, 2, 0, 0))
+ goto fail;
+
+ b = WILC_FW_REKEY_POLICY_DISABLE;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_POLICY, &b, 1, 0, 0))
+ goto fail;
+
+ w = 84600;
+ cpu_to_le32s(&w);
+ if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PERIOD, (u8 *)&w, 4, 0, 0))
+ goto fail;
+
+ w = 500;
+ cpu_to_le32s(&w);
+ if (!wilc_wlan_cfg_set(vif, 0, WID_REKEY_PACKET_COUNT, (u8 *)&w, 4, 0,
+ 0))
+ goto fail;
+
+ b = 1;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_SHORT_SLOT_ALLOWED, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = WILC_FW_ERP_PROT_SELF_CTS;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ERP_PROT_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 1;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_ENABLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_11N_OP_MODE_HT_MIXED;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OPERATING_MODE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 1;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_TXOP_PROT_DISABLE, &b, 1, 0, 0))
+ goto fail;
+
+ b = WILC_FW_OBBS_NONHT_DETECT_PROTECT_REPORT;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_OBSS_NONHT_DETECTION, &b, 1,
+ 0, 0))
+ goto fail;
+
+ b = WILC_FW_HT_PROT_RTS_CTS_NONHT;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_HT_PROT_TYPE, &b, 1, 0, 0))
+ goto fail;
+
+ b = 0;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_RIFS_PROT_ENABLE, &b, 1, 0,
+ 0))
+ goto fail;
+
+ b = 7;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0, 0))
+ goto fail;
+
+ b = 1;
+ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1,
+ 1, 0))
+ goto fail;
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static void wlan_deinitialize_threads(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+
+ wl->close = 1;
+
+ 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)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wl = vif->wilc;
+
+ if (!wl) {
+ netdev_err(dev, "wl is NULL\n");
+ return;
+ }
+
+ if (wl->initialized) {
+ netdev_info(dev, "Deinitializing wilc1000...\n");
+
+ if (!wl->dev_irq_num &&
+ 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);
+
+ wlan_deinitialize_threads(dev);
+ deinit_irq(dev);
+
+ wilc_wlan_stop(wl, vif);
+ wilc_wlan_cleanup(dev);
+
+ wl->initialized = false;
+
+ netdev_dbg(dev, "wilc1000 deinitialization Done\n");
+ } else {
+ netdev_dbg(dev, "wilc1000 is not initialized\n");
+ }
+}
+
+static int wlan_initialize_threads(struct net_device *dev)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ wilc->txq_thread = kthread_run(wilc_txq_task, (void *)wilc,
+ "%s-tx", dev->name);
+ if (IS_ERR(wilc->txq_thread)) {
+ netdev_err(dev, "couldn't create TXQ thread\n");
+ wilc->close = 1;
+ return PTR_ERR(wilc->txq_thread);
+ }
+ wait_for_completion(&wilc->txq_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;
+
+ ret = wilc_wlan_init(dev);
+ if (ret)
+ return ret;
+
+ ret = wlan_initialize_threads(dev);
+ if (ret)
+ goto fail_wilc_wlan;
+
+ if (wl->dev_irq_num && init_irq(dev)) {
+ ret = -EIO;
+ goto fail_threads;
+ }
+
+ if (!wl->dev_irq_num &&
+ wl->hif_func->enable_interrupt &&
+ wl->hif_func->enable_interrupt(wl)) {
+ ret = -EIO;
+ goto fail_irq_init;
+ }
+
+ ret = wilc_wlan_get_firmware(dev);
+ if (ret)
+ goto fail_irq_enable;
+
+ ret = wilc_firmware_download(dev);
+ if (ret)
+ goto fail_irq_enable;
+
+ ret = wilc_start_firmware(dev);
+ if (ret)
+ goto fail_irq_enable;
+
+ if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) {
+ int size;
+ char firmware_ver[WILC_MAX_FW_VERSION_STR_SIZE];
+
+ size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION,
+ firmware_ver,
+ sizeof(firmware_ver));
+ firmware_ver[size] = '\0';
+ netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver);
+ }
+
+ ret = wilc_init_fw_config(dev, vif);
+ if (ret) {
+ netdev_err(dev, "Failed to configure firmware\n");
+ goto fail_fw_start;
+ }
+ wl->initialized = true;
+ return 0;
+
+fail_fw_start:
+ wilc_wlan_stop(wl, vif);
+
+fail_irq_enable:
+ if (!wl->dev_irq_num &&
+ wl->hif_func->disable_interrupt)
+ wl->hif_func->disable_interrupt(wl);
+fail_irq_init:
+ if (wl->dev_irq_num)
+ deinit_irq(dev);
+fail_threads:
+ wlan_deinitialize_threads(dev);
+fail_wilc_wlan:
+ wilc_wlan_cleanup(dev);
+ netdev_err(dev, "WLAN initialization FAILED\n");
+ } else {
+ netdev_dbg(dev, "wilc1000 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;
+ int ret = 0;
+ struct mgmt_frame_regs mgmt_regs = {};
+
+ if (!wl || !wl->dev) {
+ netdev_err(ndev, "device not ready\n");
+ return -ENODEV;
+ }
+
+ netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev);
+
+ ret = wilc_init_host_int(ndev);
+ if (ret)
+ return ret;
+
+ ret = wilc_wlan_initialize(ndev, vif);
+ if (ret) {
+ wilc_deinit_host_int(ndev);
+ return ret;
+ }
+
+ wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype,
+ vif->idx);
+
+ netdev_dbg(ndev, "Mac address: %pM\n", ndev->dev_addr);
+ ret = wilc_set_mac_address(vif, ndev->dev_addr);
+ if (ret) {
+ netdev_err(ndev, "Failed to enforce MAC address in chip");
+ wilc_deinit_host_int(ndev);
+ if (!wl->open_ifcs)
+ wilc_wlan_deinitialize(ndev);
+ return ret;
+ }
+
+ mgmt_regs.interface_stypes = vif->mgmt_reg_stypes;
+ /* so we detect a change */
+ vif->mgmt_reg_stypes = 0;
+ wilc_update_mgmt_frame_registrations(vif->ndev->ieee80211_ptr->wiphy,
+ vif->ndev->ieee80211_ptr,
+ &mgmt_regs);
+ netif_wake_queue(ndev);
+ wl->open_ifcs++;
+ 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 wilc *wilc = vif->wilc;
+ struct sockaddr *addr = (struct sockaddr *)p;
+ unsigned char mac_addr[ETH_ALEN];
+ struct wilc_vif *tmp_vif;
+ int srcu_idx;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (!vif->mac_opened) {
+ eth_commit_mac_addr_change(dev, p);
+ return 0;
+ }
+
+ /* Verify MAC Address is not already in use: */
+
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ wilc_for_each_vif(wilc, tmp_vif) {
+ wilc_get_mac_address(tmp_vif, mac_addr);
+ if (ether_addr_equal(addr->sa_data, mac_addr)) {
+ if (vif != tmp_vif) {
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+ return -EADDRNOTAVAIL;
+ }
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+ return 0;
+ }
+ }
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+
+ result = wilc_set_mac_address(vif, addr->sa_data);
+ if (result)
+ return result;
+
+ eth_commit_mac_addr_change(dev, p);
+ 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;
+
+ if (dev->flags & IFF_PROMISC)
+ return;
+
+ if (dev->flags & IFF_ALLMULTI ||
+ dev->mc.count > WILC_MULTICAST_TABLE_SIZE) {
+ wilc_setup_multicast_filter(vif, 0, 0, NULL);
+ return;
+ }
+
+ if (dev->mc.count == 0) {
+ 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);
+ netdev_dbg(dev, "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;
+
+ 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;
+
+ if (skb->dev != ndev) {
+ netdev_err(ndev, "Packet not destined to this device\n");
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ tx_data = kmalloc_obj(*tx_data, GFP_ATOMIC);
+ if (!tx_data) {
+ 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;
+
+ vif->netstats.tx_packets++;
+ vif->netstats.tx_bytes += tx_data->size;
+ queue_count = wilc_wlan_txq_add_net_pkt(ndev, tx_data,
+ tx_data->buff, tx_data->size,
+ wilc_tx_complete);
+
+ if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) {
+ int srcu_idx;
+ struct wilc_vif *vif;
+
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ wilc_for_each_vif(wilc, vif) {
+ if (vif->mac_opened)
+ netif_stop_queue(vif->ndev);
+ }
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+ }
+
+ 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;
+
+ netdev_dbg(ndev, "Mac close\n");
+
+ if (wl->open_ifcs > 0)
+ wl->open_ifcs--;
+ else
+ return 0;
+
+ if (vif->ndev) {
+ netif_stop_queue(vif->ndev);
+
+ wilc_handle_disconnect(vif);
+ wilc_deinit_host_int(vif->ndev);
+ }
+
+ if (wl->open_ifcs == 0) {
+ netdev_dbg(ndev, "Deinitializing wilc1000\n");
+ wl->close = 1;
+ wilc_wlan_deinitialize(ndev);
+ }
+
+ vif->mac_opened = 0;
+
+ return 0;
+}
+
+void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
+ u32 pkt_offset)
+{
+ unsigned char *buff_to_send = NULL;
+ struct net_device *wilc_netdev;
+ unsigned int frame_len = 0;
+ struct wilc_vif *vif;
+ struct sk_buff *skb;
+ int srcu_idx;
+ int stats;
+
+ if (!wilc)
+ return;
+
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ wilc_netdev = get_if_handler(wilc, buff);
+ if (!wilc_netdev)
+ goto out;
+
+ buff += pkt_offset;
+ vif = netdev_priv(wilc_netdev);
+
+ if (size > 0) {
+ frame_len = size;
+ buff_to_send = buff;
+
+ skb = dev_alloc_skb(frame_len);
+ if (!skb)
+ goto out;
+
+ skb->dev = wilc_netdev;
+
+ skb_put_data(skb, buff_to_send, frame_len);
+
+ skb->protocol = eth_type_trans(skb, wilc_netdev);
+ vif->netstats.rx_packets++;
+ vif->netstats.rx_bytes += frame_len;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ stats = netif_rx(skb);
+ netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats);
+ }
+out:
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+}
+
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth)
+{
+ int srcu_idx;
+ struct wilc_vif *vif;
+
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ wilc_for_each_vif(wilc, vif) {
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buff;
+ u16 type = le16_to_cpup((__le16 *)buff);
+ u32 type_bit = BIT(type >> 4);
+ u32 auth_bit = BIT(IEEE80211_STYPE_AUTH >> 4);
+
+ if ((vif->mgmt_reg_stypes & auth_bit &&
+ ieee80211_is_auth(mgmt->frame_control)) &&
+ vif->iftype == WILC_STATION_MODE && is_auth) {
+ wilc_wfi_mgmt_frame_rx(vif, buff, size);
+ break;
+ }
+
+ if (vif->priv.p2p_listen_state &&
+ vif->mgmt_reg_stypes & type_bit)
+ wilc_wfi_p2p_rx(vif, buff, size);
+
+ if (vif->monitor_flag)
+ wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size);
+ }
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+}
+
+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,
+};
+
+void wilc_netdev_cleanup(struct wilc *wilc)
+{
+ struct wilc_vif *vif, *vif_tmp;
+
+ if (!wilc)
+ return;
+
+ if (wilc->firmware) {
+ release_firmware(wilc->firmware);
+ wilc->firmware = NULL;
+ }
+
+ list_for_each_entry_safe(vif, vif_tmp, &wilc->vif_list, list) {
+ mutex_lock(&wilc->vif_mutex);
+ list_del_rcu(&vif->list);
+ wilc->vif_num--;
+ mutex_unlock(&wilc->vif_mutex);
+ synchronize_srcu(&wilc->srcu);
+ if (vif->ndev)
+ unregister_netdev(vif->ndev);
+ }
+
+ wilc_wfi_deinit_mon_interface(wilc, false);
+ destroy_workqueue(wilc->hif_workqueue);
+
+ wilc_wlan_cfg_deinit(wilc);
+ wlan_deinit_locks(wilc);
+}
+EXPORT_SYMBOL_GPL(wilc_netdev_cleanup);
+
+static u8 wilc_get_available_idx(struct wilc *wl)
+{
+ int idx = 0;
+ struct wilc_vif *vif;
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ wilc_for_each_vif(wl, vif) {
+ if (vif->idx == 0)
+ idx = 1;
+ else
+ idx = 0;
+ }
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return idx;
+}
+
+struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
+ int vif_type, enum nl80211_iftype type,
+ bool rtnl_locked)
+{
+ u8 mac_address[ETH_ALEN];
+ struct net_device *ndev;
+ struct wilc_vif *vif;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(*vif));
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ vif = netdev_priv(ndev);
+ ndev->ieee80211_ptr = &vif->priv.wdev;
+ strcpy(ndev->name, name);
+ vif->wilc = wl;
+ vif->ndev = ndev;
+ ndev->ml_priv = vif;
+
+ ndev->netdev_ops = &wilc_netdev_ops;
+
+ SET_NETDEV_DEV(ndev, wiphy_dev(wl->wiphy));
+
+ vif->priv.wdev.wiphy = wl->wiphy;
+ vif->priv.wdev.netdev = ndev;
+ vif->priv.wdev.iftype = type;
+ vif->priv.dev = ndev;
+
+ ndev->needs_free_netdev = true;
+ vif->iftype = vif_type;
+ vif->idx = wilc_get_available_idx(wl);
+ vif->mac_opened = 0;
+
+ memcpy(mac_address, wl->nv_mac_address, ETH_ALEN);
+ /* WILC firmware uses locally administered MAC address for the
+ * second virtual interface (bit 1 of first byte set), but
+ * since it is possibly not loaded/running yet, reproduce this behavior
+ * in the driver during interface creation.
+ */
+ if (vif->idx)
+ mac_address[0] |= 0x2;
+
+ eth_hw_addr_set(vif->ndev, mac_address);
+
+ mutex_lock(&wl->vif_mutex);
+ list_add_tail_rcu(&vif->list, &wl->vif_list);
+ wl->vif_num += 1;
+ mutex_unlock(&wl->vif_mutex);
+ synchronize_srcu(&wl->srcu);
+
+ if (rtnl_locked)
+ ret = cfg80211_register_netdevice(ndev);
+ else
+ ret = register_netdev(ndev);
+
+ if (ret) {
+ ret = -EFAULT;
+ goto error_remove_vif;
+ }
+
+ return vif;
+
+error_remove_vif:
+ mutex_lock(&wl->vif_mutex);
+ list_del_rcu(&vif->list);
+ wl->vif_num -= 1;
+ mutex_unlock(&wl->vif_mutex);
+ synchronize_srcu(&wl->srcu);
+ free_netdev(ndev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(wilc_netdev_ifc_init);
+
+MODULE_DESCRIPTION("Atmel WILC1000 core wireless driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(WILC1000_FW(WILC1000_API_VER));
+MODULE_FIRMWARE(WILC3000_FW(WILC3000_API_VER));
diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.h b/drivers/net/wireless/microchip/wilc1000/netdev.h
new file mode 100644
index 000000000..95bc8b8fe
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/netdev.h
@@ -0,0 +1,305 @@
+/* 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/tcp.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/if_arp.h>
+#include <linux/gpio/consumer.h>
+#include <linux/rculist.h>
+#include <uapi/linux/if_ether.h>
+
+#include "hif.h"
+#include "wlan.h"
+#include "wlan_cfg.h"
+
+#define FLOW_CONTROL_LOWER_THRESHOLD 128
+#define FLOW_CONTROL_UPPER_THRESHOLD 256
+
+#define PMKID_FOUND 1
+#define NUM_STA_ASSOCIATED 8
+
+#define TCP_ACK_FILTER_LINK_SPEED_THRESH 54
+#define DEFAULT_LINK_SPEED 72
+
+#define TX_BACKOFF_WEIGHT_MS 1
+
+#define wilc_for_each_vif(w, v) \
+ struct wilc *_w = w; \
+ list_for_each_entry_srcu(v, &_w->vif_list, list, \
+ srcu_read_lock_held(&_w->srcu))
+
+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 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;
+};
+
+static const u32 wilc_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC
+};
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = NL80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+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;
+
+ /* 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];
+ struct wilc_wfi_key *wilc_igtk[2];
+ u8 wilc_groupkey;
+
+ /* mutexes */
+ struct mutex scan_req_lock;
+ bool p2p_listen_state;
+ int scanned_cnt;
+
+ u64 inc_roc_cookie;
+};
+
+#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 wilc_vif {
+ u8 idx;
+ u8 iftype;
+ int monitor_flag;
+ int mac_opened;
+ u32 mgmt_reg_stypes;
+ struct net_device_stats netstats;
+ struct wilc *wilc;
+ u8 bssid[ETH_ALEN];
+ struct host_if_drv *hif_drv;
+ struct net_device *ndev;
+ struct timer_list during_ip_timer;
+ struct timer_list periodic_rssi;
+ struct rf_info periodic_stat;
+ struct tcp_ack_filter ack_filter;
+ bool connecting;
+ struct wilc_priv priv;
+ struct list_head list;
+ struct cfg80211_bss *bss;
+ struct cfg80211_external_auth_params auth;
+};
+
+struct wilc_tx_queue_status {
+ u8 buffer[AC_BUFFER_SIZE];
+ u16 end_index;
+ u16 cnt[NQUEUES];
+ u16 sum;
+ bool initialized;
+};
+
+struct wilc {
+ struct wiphy *wiphy;
+ const struct wilc_hif_func *hif_func;
+ int io_type;
+ s8 mac_status;
+ struct clk *rtc_clk;
+ bool initialized;
+ u32 chipid;
+ bool power_save_mode;
+ int dev_irq_num;
+ int close;
+ u8 vif_num;
+ struct list_head vif_list;
+
+ /* protect vif list */
+ struct mutex vif_mutex;
+ /* Sleepable RCU struct to manipulate vif list. Sleepable version is
+ * needed over the classic RCU version because the driver's current
+ * design involves some sleeping code while manipulating a vif
+ * retrieved from vif list (so in a SRCU critical section), like:
+ * - sending commands to the chip, using info from retrieved vif
+ * - registering a new monitoring net device
+ */
+ struct srcu_struct srcu;
+ 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 task_struct *txq_thread;
+
+ int quit;
+
+ /* lock to protect issue of wid command to firmware */
+ 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;
+ u32 *vmm_table;
+
+ struct txq_handle txq[NQUEUES];
+ int txq_entries;
+
+ struct wilc_tx_queue_status tx_q_limit;
+ struct rxq_entry_t rxq_head;
+
+ const struct firmware *firmware;
+
+ struct device *dev;
+
+ 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 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)];
+ u8 nv_mac_address[ETH_ALEN];
+};
+
+struct wilc_wfi_mon_priv {
+ struct net_device *real_ndev;
+};
+
+void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset);
+void wilc_mac_indicate(struct wilc *wilc);
+void wilc_netdev_cleanup(struct wilc *wilc);
+void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth);
+void wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid,
+ u8 mode);
+struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
+ int vif_type, enum nl80211_iftype type,
+ bool rtnl_locked);
+#endif
diff --git a/drivers/net/wireless/microchip/wilc1000/sdio.c b/drivers/net/wireless/microchip/wilc1000/sdio.c
new file mode 100644
index 000000000..67dea1360
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/sdio.c
@@ -0,0 +1,1076 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio.h>
+#include <linux/of_irq.h>
+
+#include "netdev.h"
+#include "cfg80211.h"
+
+#define SDIO_MODALIAS "wilc1000_sdio"
+
+static const struct sdio_device_id wilc_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MICROCHIP_WILC, SDIO_DEVICE_ID_MICROCHIP_WILC1000) },
+ { },
+};
+MODULE_DEVICE_TABLE(sdio, wilc_sdio_ids);
+
+#define WILC_SDIO_BLOCK_SIZE 512
+
+static int wilc_sdio_init(struct wilc *wilc, bool resume);
+static int wilc_sdio_deinit(struct wilc *wilc);
+
+struct wilc_sdio {
+ bool irq_gpio;
+ u32 block_size;
+ bool isinit;
+ u8 *cmd53_buf;
+};
+
+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;
+ bool use_global_buf;
+};
+
+static const struct wilc_hif_func wilc_hif_sdio;
+
+static void wilc_sdio_interrupt(struct sdio_func *func)
+{
+ sdio_release_host(func);
+ wilc_handle_isr(sdio_get_drvdata(func));
+ sdio_claim_host(func);
+}
+
+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;
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ u8 *buf = cmd->buffer;
+
+ 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->use_global_buf) {
+ if (size > sizeof(u32)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ buf = sdio_priv->cmd53_buf;
+ }
+
+ if (cmd->read_write) { /* write */
+ if (cmd->use_global_buf)
+ memcpy(buf, cmd->buffer, size);
+
+ ret = sdio_memcpy_toio(func, cmd->address, buf, size);
+ } else { /* read */
+ ret = sdio_memcpy_fromio(func, buf, cmd->address, size);
+
+ if (cmd->use_global_buf)
+ memcpy(cmd->buffer, buf, size);
+ }
+out:
+ 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_sdio *sdio_priv;
+ struct wilc_vif *vif;
+ struct wilc *wilc;
+ int ret;
+
+
+ sdio_priv = kzalloc_obj(*sdio_priv);
+ if (!sdio_priv)
+ return -ENOMEM;
+
+ sdio_priv->cmd53_buf = kzalloc(sizeof(u32), GFP_KERNEL);
+ if (!sdio_priv->cmd53_buf) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ ret = wilc_cfg80211_init(&wilc, &func->dev, WILC_HIF_SDIO,
+ &wilc_hif_sdio);
+ if (ret)
+ goto free;
+
+ if (IS_ENABLED(CONFIG_WILC1000_HW_OOB_INTR)) {
+ struct device_node *np = func->card->dev.of_node;
+ int irq_num = of_irq_get(np, 0);
+
+ if (irq_num > 0) {
+ wilc->dev_irq_num = irq_num;
+ sdio_priv->irq_gpio = true;
+ }
+ }
+
+ sdio_set_drvdata(func, wilc);
+ wilc->bus_data = sdio_priv;
+ wilc->dev = &func->dev;
+
+ wilc->rtc_clk = devm_clk_get_optional_enabled(&func->card->dev, "rtc");
+ if (IS_ERR(wilc->rtc_clk)) {
+ ret = PTR_ERR(wilc->rtc_clk);
+ goto dispose_irq;
+ }
+
+ wilc_sdio_init(wilc, false);
+
+ ret = wilc_get_chipid(wilc);
+ if (ret)
+ goto dispose_irq;
+
+ ret = wilc_cfg80211_register(wilc);
+ if (ret)
+ goto dispose_irq;
+
+ ret = wilc_load_mac_from_nv(wilc);
+ if (ret) {
+ pr_err("Can not retrieve MAC address from chip\n");
+ goto unregister_wiphy;
+ }
+
+ wilc_sdio_deinit(wilc);
+
+ vif = wilc_netdev_ifc_init(wilc, "wlan%d", WILC_STATION_MODE,
+ NL80211_IFTYPE_STATION, false);
+ if (IS_ERR(vif)) {
+ ret = PTR_ERR(vif);
+ goto unregister_wiphy;
+ }
+
+ dev_info(&func->dev, "Driver Initializing success\n");
+ return 0;
+
+unregister_wiphy:
+ wiphy_unregister(wilc->wiphy);
+dispose_irq:
+ irq_dispose_mapping(wilc->dev_irq_num);
+ wilc_netdev_cleanup(wilc);
+ wiphy_free(wilc->wiphy);
+free:
+ kfree(sdio_priv->cmd53_buf);
+ kfree(sdio_priv);
+ return ret;
+}
+
+static void wilc_sdio_remove(struct sdio_func *func)
+{
+ struct wilc *wilc = sdio_get_drvdata(func);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+
+ wilc_netdev_cleanup(wilc);
+ wiphy_unregister(wilc->wiphy);
+ wiphy_free(wilc->wiphy);
+ kfree(sdio_priv->cmd53_buf);
+ kfree(sdio_priv);
+}
+
+static int wilc_sdio_reset(struct wilc *wilc)
+{
+ struct sdio_cmd52 cmd;
+ int ret;
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = SDIO_CCCR_ABORT;
+ cmd.data = WILC_SDIO_CCCR_ABORT_RESET;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, reset cmd ...\n");
+ return ret;
+ }
+ return 0;
+}
+
+static bool wilc_sdio_is_init(struct wilc *wilc)
+{
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+
+ return sdio_priv->isinit;
+}
+
+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_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;
+
+ 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);
+}
+
+/********************************************
+ *
+ * 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 = WILC_SDIO_FBR_CSA_REG;
+ cmd.data = (u8)adr;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
+ cmd.address);
+ return ret;
+ }
+
+ cmd.address = WILC_SDIO_FBR_CSA_REG + 1;
+ cmd.data = (u8)(adr >> 8);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
+ cmd.address);
+ return ret;
+ }
+
+ cmd.address = WILC_SDIO_FBR_CSA_REG + 2;
+ cmd.data = (u8)(adr >> 16);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
+ cmd.address);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wilc_sdio_set_block_size(struct wilc *wilc, u8 func_num,
+ 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 = SDIO_FBR_BASE(func_num) + SDIO_CCCR_BLKSIZE;
+ cmd.data = (u8)block_size;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
+ cmd.address);
+ return ret;
+ }
+
+ cmd.address = SDIO_FBR_BASE(func_num) + SDIO_CCCR_BLKSIZE + 1;
+ cmd.data = (u8)(block_size >> 8);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set %04x data...\n",
+ cmd.address);
+ return ret;
+ }
+
+ 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) { /* only vendor specific registers */
+ 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, read reg (%08x) ...\n", addr);
+ } else {
+ struct sdio_cmd53 cmd;
+
+ /**
+ * set the AHB address
+ **/
+ ret = wilc_sdio_set_func0_csa_address(wilc, addr);
+ if (ret)
+ return ret;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.address = WILC_SDIO_FBR_DATA_REG;
+ cmd.block_mode = 0;
+ cmd.increment = 1;
+ cmd.count = sizeof(u32);
+ cmd.buffer = (u8 *)&data;
+ cmd.use_global_buf = true;
+ 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);
+ }
+
+ return ret;
+}
+
+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) {
+ /**
+ * func 0 access
+ **/
+ cmd.function = 0;
+ cmd.address = WILC_SDIO_FBR_DATA_REG;
+ } else {
+ /**
+ * func 1 access
+ **/
+ cmd.function = 1;
+ cmd.address = WILC_SDIO_F1_DATA_REG;
+ }
+
+ size = ALIGN(size, 4);
+ nblk = size / block_size;
+ nleft = size % block_size;
+
+ cmd.use_global_buf = false;
+ if (nblk > 0) {
+ cmd.block_mode = 1;
+ cmd.increment = 1;
+ cmd.count = nblk;
+ cmd.buffer = buf;
+ cmd.block_size = block_size;
+ if (addr > 0) {
+ ret = wilc_sdio_set_func0_csa_address(wilc, addr);
+ if (ret)
+ return ret;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], block send...\n", addr);
+ return ret;
+ }
+ 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) {
+ ret = wilc_sdio_set_func0_csa_address(wilc, addr);
+ if (ret)
+ return ret;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], bytes send...\n", addr);
+ return ret;
+ }
+ }
+
+ 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) { /* only vendor specific registers */
+ 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);
+ return ret;
+ }
+ *data = cmd.data;
+ } else {
+ struct sdio_cmd53 cmd;
+
+ ret = wilc_sdio_set_func0_csa_address(wilc, addr);
+ if (ret)
+ return ret;
+
+ cmd.read_write = 0;
+ cmd.function = 0;
+ cmd.address = WILC_SDIO_FBR_DATA_REG;
+ cmd.block_mode = 0;
+ cmd.increment = 1;
+ cmd.count = sizeof(u32);
+ cmd.buffer = (u8 *)data;
+ cmd.use_global_buf = true;
+
+ 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);
+ return ret;
+ }
+ }
+
+ le32_to_cpus(data);
+ 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) {
+ /**
+ * func 0 access
+ **/
+ cmd.function = 0;
+ cmd.address = WILC_SDIO_FBR_DATA_REG;
+ } else {
+ /**
+ * func 1 access
+ **/
+ cmd.function = 1;
+ cmd.address = WILC_SDIO_F1_DATA_REG;
+ }
+
+ size = ALIGN(size, 4);
+ nblk = size / block_size;
+ nleft = size % block_size;
+
+ cmd.use_global_buf = false;
+ if (nblk > 0) {
+ cmd.block_mode = 1;
+ cmd.increment = 1;
+ cmd.count = nblk;
+ cmd.buffer = buf;
+ cmd.block_size = block_size;
+ if (addr > 0) {
+ ret = wilc_sdio_set_func0_csa_address(wilc, addr);
+ if (ret)
+ return ret;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], block read...\n", addr);
+ return ret;
+ }
+ 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) {
+ ret = wilc_sdio_set_func0_csa_address(wilc, addr);
+ if (ret)
+ return ret;
+ }
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], bytes read...\n", addr);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/********************************************
+ *
+ * Bus interfaces
+ *
+ ********************************************/
+
+static int wilc_sdio_deinit(struct wilc *wilc)
+{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ struct wilc_sdio *sdio_priv = wilc->bus_data;
+ struct sdio_cmd52 cmd;
+ int ret;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+
+ /* Disable all functions interrupts */
+ cmd.address = SDIO_CCCR_IENx;
+ cmd.data = 0;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed to disable functions interrupts\n");
+ return ret;
+ }
+
+ /* Disable all functions */
+ cmd.address = SDIO_CCCR_IOEx;
+ cmd.data = 0;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed to reset all functions\n");
+ return ret;
+ }
+
+ /* Disable CSA */
+ cmd.read_write = 0;
+ cmd.address = SDIO_FBR_BASE(1);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed to read CSA for function 1\n");
+ return ret;
+ }
+ cmd.read_write = 1;
+ cmd.address = SDIO_FBR_BASE(1);
+ cmd.data &= ~SDIO_FBR_ENABLE_CSA;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed to disable CSA for function 1\n");
+ return ret;
+ }
+
+ sdio_priv->isinit = false;
+ return 0;
+}
+
+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;
+
+ /**
+ * function 0 csa enable
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+ cmd.address = SDIO_FBR_BASE(1);
+ cmd.data = SDIO_FBR_ENABLE_CSA;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, enable csa...\n");
+ return ret;
+ }
+
+ /**
+ * function 0 block size
+ **/
+ ret = wilc_sdio_set_block_size(wilc, 0, WILC_SDIO_BLOCK_SIZE);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n");
+ return ret;
+ }
+ sdio_priv->block_size = WILC_SDIO_BLOCK_SIZE;
+
+ /**
+ * enable func1 IO
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+ cmd.address = SDIO_CCCR_IOEx;
+ cmd.data = WILC_SDIO_CCCR_IO_EN_FUNC1;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Fail cmd 52, set IOE register...\n");
+ return ret;
+ }
+
+ /**
+ * make sure func 1 is up
+ **/
+ cmd.read_write = 0;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = SDIO_CCCR_IORx;
+ 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");
+ return ret;
+ }
+ if (cmd.data == WILC_SDIO_CCCR_IO_EN_FUNC1)
+ break;
+ } while (loop--);
+
+ if (loop <= 0) {
+ dev_err(&func->dev, "Fail func 1 is not ready...\n");
+ return -EINVAL;
+ }
+
+ /**
+ * func 1 is ready, set func 1 block size
+ **/
+ ret = wilc_sdio_set_block_size(wilc, 1, WILC_SDIO_BLOCK_SIZE);
+ if (ret) {
+ dev_err(&func->dev, "Fail set func 1 block size...\n");
+ return ret;
+ }
+
+ /**
+ * func 1 interrupt enable
+ **/
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 1;
+ cmd.address = SDIO_CCCR_IENx;
+ cmd.data = WILC_SDIO_CCCR_IEN_MASTER | WILC_SDIO_CCCR_IEN_FUNC1;
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, set IEN register...\n");
+ return ret;
+ }
+
+ sdio_priv->isinit = true;
+ 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 = WILC_SDIO_INTERRUPT_DATA_SZ_REG;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+ tmp = cmd.data;
+
+ cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG + 1;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+ tmp |= (cmd.data << 8);
+
+ *size = tmp;
+ return 0;
+}
+
+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;
+ u8 irq_flags;
+ struct sdio_cmd52 cmd;
+
+ wilc_sdio_read_size(wilc, &tmp);
+
+ /**
+ * Read IRQ flags
+ **/
+ if (!sdio_priv->irq_gpio) {
+ cmd.function = 1;
+ cmd.address = WILC_SDIO_EXT_IRQ_FLAG_REG;
+ } else {
+ cmd.function = 0;
+ cmd.address = is_wilc1000(wilc->chipid) ?
+ WILC1000_SDIO_IRQ_FLAG_REG :
+ WILC3000_SDIO_IRQ_FLAG_REG;
+ }
+ cmd.raw = 0;
+ cmd.read_write = 0;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+ irq_flags = cmd.data;
+
+ if (sdio_priv->irq_gpio)
+ irq_flags &= is_wilc1000(wilc->chipid) ? 0x1f : 0x0f;
+
+ tmp |= FIELD_PREP(IRG_FLAGS_MASK, cmd.data);
+
+ if (FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags))
+ dev_err(&func->dev, "Unexpected interrupt (1) int=%lx\n",
+ FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags));
+
+ *int_status = tmp;
+
+ return 0;
+}
+
+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 (sdio_priv->irq_gpio)
+ reg = val & (BIT(MAX_NUM_INT) - 1);
+
+ if (is_wilc1000(wilc->chipid)) {
+ /* 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);
+ } else {
+ if (sdio_priv->irq_gpio && reg) {
+ struct sdio_cmd52 cmd;
+
+ cmd.read_write = 1;
+ cmd.function = 0;
+ cmd.raw = 0;
+ cmd.address = WILC3000_SDIO_IRQ_FLAG_REG;
+ cmd.data = reg;
+
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xfe data (%d) ...\n",
+ __LINE__);
+ return ret;
+ }
+ }
+
+ reg = 0;
+ /* 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 = is_wilc1000(wilc->chipid) ?
+ WILC1000_SDIO_IRQ_CLEAR_FLAG_REG :
+ WILC3000_SDIO_VMM_TBL_CTRL_REG;
+ cmd.data = reg;
+
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set (%02x) data (%d) ...\n",
+ cmd.address, __LINE__);
+ return ret;
+ }
+ }
+ 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;
+
+ if (nint > MAX_NUM_INT) {
+ dev_err(&func->dev, "Too many interrupts (%d)...\n", nint);
+ return -EINVAL;
+ }
+
+ if (sdio_priv->irq_gpio) {
+ u32 reg;
+ int ret, i;
+
+ /**
+ * 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 ret;
+ }
+ 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 ret;
+ }
+
+ /**
+ * 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 ret;
+ }
+
+ 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 ret;
+ }
+ 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 ret;
+ }
+
+ 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 ret;
+ }
+ }
+ }
+ return 0;
+}
+
+/* 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_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct wilc *wilc = sdio_get_drvdata(func);
+ int ret;
+
+ dev_info(dev, "sdio suspend\n");
+
+ if (!wilc->initialized)
+ return 0;
+
+ if (!IS_ERR(wilc->rtc_clk))
+ clk_disable_unprepare(wilc->rtc_clk);
+
+ ret = host_sleep_notify(wilc);
+ if (ret) {
+ clk_prepare_enable(wilc->rtc_clk);
+ return ret;
+ }
+
+ wilc_sdio_disable_interrupt(wilc);
+
+ return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+}
+
+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(dev, "sdio resume\n");
+
+ if (!wilc->initialized)
+ return 0;
+
+ if (!IS_ERR(wilc->rtc_clk))
+ clk_prepare_enable(wilc->rtc_clk);
+
+ wilc_sdio_init(wilc, true);
+ wilc_sdio_enable_interrupt(wilc);
+
+ return host_wakeup_notify(wilc);
+}
+
+static const struct of_device_id wilc_of_match[] = {
+ { .compatible = "microchip,wilc1000", },
+ { /* 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_sdio_driver(wilc_sdio_driver);
+
+MODULE_DESCRIPTION("Atmel WILC1000 SDIO wireless driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/microchip/wilc1000/spi.c b/drivers/net/wireless/microchip/wilc1000/spi.c
new file mode 100644
index 000000000..e40b829f7
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/spi.c
@@ -0,0 +1,1374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/crc7.h>
+#include <linux/crc-itu-t.h>
+#include <linux/gpio/consumer.h>
+
+#include "netdev.h"
+#include "cfg80211.h"
+
+#define SPI_MODALIAS "wilc1000_spi"
+
+static bool enable_crc7; /* protect SPI commands with CRC7 */
+module_param(enable_crc7, bool, 0644);
+MODULE_PARM_DESC(enable_crc7,
+ "Enable CRC7 checksum to protect command transfers\n"
+ "\t\t\tagainst corruption during the SPI transfer.\n"
+ "\t\t\tCommand transfers are short and the CPU-cycle cost\n"
+ "\t\t\tof enabling this is small.");
+
+static bool enable_crc16; /* protect SPI data with CRC16 */
+module_param(enable_crc16, bool, 0644);
+MODULE_PARM_DESC(enable_crc16,
+ "Enable CRC16 checksum to protect data transfers\n"
+ "\t\t\tagainst corruption during the SPI transfer.\n"
+ "\t\t\tData transfers can be large and the CPU-cycle cost\n"
+ "\t\t\tof enabling this may be substantial.");
+
+/*
+ * For CMD_SINGLE_READ and CMD_INTERNAL_READ, WILC may insert one or
+ * more zero bytes between the command response and the DATA Start tag
+ * (0xf3). This behavior appears to be undocumented in "ATWILC1000
+ * USER GUIDE" (https://tinyurl.com/4hhshdts) but we have observed 1-4
+ * zero bytes when the SPI bus operates at 48MHz and none when it
+ * operates at 1MHz.
+ */
+#define WILC_SPI_RSP_HDR_EXTRA_DATA 8
+
+struct wilc_spi {
+ bool isinit; /* true if wilc_spi_init was successful */
+ bool probing_crc; /* true if we're probing chip's CRC config */
+ bool crc7_enabled; /* true if crc7 is currently enabled */
+ bool crc16_enabled; /* true if crc16 is currently enabled */
+ struct wilc_gpios {
+ struct gpio_desc *enable; /* ENABLE GPIO or NULL */
+ struct gpio_desc *reset; /* RESET GPIO or NULL */
+ } gpios;
+};
+
+static const struct wilc_hif_func wilc_hif_spi;
+
+static int wilc_spi_reset(struct wilc *wilc);
+static int wilc_spi_configure_bus_protocol(struct wilc *wilc);
+static int wilc_validate_chipid(struct wilc *wilc);
+
+/********************************************
+ *
+ * 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 SPI_RETRY_MAX_LIMIT 10
+#define SPI_ENABLE_VMM_RETRY_LIMIT 2
+
+/* SPI response fields (section 11.1.2 in ATWILC1000 User Guide): */
+#define RSP_START_FIELD GENMASK(7, 4)
+#define RSP_TYPE_FIELD GENMASK(3, 0)
+
+/* SPI response values for the response fields: */
+#define RSP_START_TAG 0xc
+#define RSP_TYPE_FIRST_PACKET 0x1
+#define RSP_TYPE_INNER_PACKET 0x2
+#define RSP_TYPE_LAST_PACKET 0x3
+#define RSP_STATE_NO_ERROR 0x00
+
+#define PROTOCOL_REG_PKT_SZ_MASK GENMASK(6, 4)
+#define PROTOCOL_REG_CRC16_MASK GENMASK(3, 3)
+#define PROTOCOL_REG_CRC7_MASK GENMASK(2, 2)
+
+/*
+ * The SPI data packet size may be any integer power of two in the
+ * range from 256 to 8192 bytes.
+ */
+#define DATA_PKT_LOG_SZ_MIN 8 /* 256 B */
+#define DATA_PKT_LOG_SZ_MAX 13 /* 8 KiB */
+
+/*
+ * Select the data packet size (log2 of number of bytes): Use the
+ * maximum data packet size. We only retransmit complete packets, so
+ * there is no benefit from using smaller data packets.
+ */
+#define DATA_PKT_LOG_SZ DATA_PKT_LOG_SZ_MAX
+#define DATA_PKT_SZ (1 << DATA_PKT_LOG_SZ)
+
+#define WILC_SPI_COMMAND_STAT_SUCCESS 0
+#define WILC_GET_RESP_HDR_START(h) (((h) >> 4) & 0xf)
+
+struct wilc_spi_cmd {
+ u8 cmd_type;
+ union {
+ struct {
+ u8 addr[3];
+ u8 crc[];
+ } __packed simple_cmd;
+ struct {
+ u8 addr[3];
+ u8 size[2];
+ u8 crc[];
+ } __packed dma_cmd;
+ struct {
+ u8 addr[3];
+ u8 size[3];
+ u8 crc[];
+ } __packed dma_cmd_ext;
+ struct {
+ u8 addr[2];
+ __be32 data;
+ u8 crc[];
+ } __packed internal_w_cmd;
+ struct {
+ u8 addr[3];
+ __be32 data;
+ u8 crc[];
+ } __packed w_cmd;
+ } u;
+} __packed;
+
+struct wilc_spi_read_rsp_data {
+ u8 header;
+ u8 data[4];
+ u8 crc[];
+} __packed;
+
+struct wilc_spi_rsp_data {
+ u8 rsp_cmd_type;
+ u8 status;
+ u8 data[];
+} __packed;
+
+struct wilc_spi_special_cmd_rsp {
+ u8 skip_byte;
+ u8 rsp_cmd_type;
+ u8 status;
+} __packed;
+
+static int wilc_parse_gpios(struct wilc *wilc)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ struct wilc_gpios *gpios = &spi_priv->gpios;
+
+ /* get ENABLE pin and deassert it (if it is defined): */
+ gpios->enable = devm_gpiod_get_optional(&spi->dev,
+ "enable", GPIOD_OUT_LOW);
+ /* get RESET pin and assert it (if it is defined): */
+ if (gpios->enable) {
+ /* if enable pin exists, reset must exist as well */
+ gpios->reset = devm_gpiod_get(&spi->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpios->reset)) {
+ dev_err(&spi->dev, "missing reset gpio.\n");
+ return PTR_ERR(gpios->reset);
+ }
+ } else {
+ gpios->reset = devm_gpiod_get_optional(&spi->dev,
+ "reset", GPIOD_OUT_HIGH);
+ }
+ return 0;
+}
+
+static void wilc_wlan_power(struct wilc *wilc, bool on)
+{
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ struct wilc_gpios *gpios = &spi_priv->gpios;
+
+ if (on) {
+ /* assert ENABLE: */
+ gpiod_set_value(gpios->enable, 1);
+ mdelay(5);
+ /* deassert RESET: */
+ gpiod_set_value(gpios->reset, 0);
+ } else {
+ /* assert RESET: */
+ gpiod_set_value(gpios->reset, 1);
+ /* deassert ENABLE: */
+ gpiod_set_value(gpios->enable, 0);
+ }
+}
+
+static int wilc_bus_probe(struct spi_device *spi)
+{
+ struct wilc_spi *spi_priv;
+ struct wilc_vif *vif;
+ struct wilc *wilc;
+ int ret;
+
+ spi_priv = kzalloc_obj(*spi_priv);
+ if (!spi_priv)
+ return -ENOMEM;
+
+ ret = wilc_cfg80211_init(&wilc, &spi->dev, WILC_HIF_SPI, &wilc_hif_spi);
+ if (ret)
+ goto free;
+
+ spi_set_drvdata(spi, wilc);
+ wilc->dev = &spi->dev;
+ wilc->bus_data = spi_priv;
+ wilc->dev_irq_num = spi->irq;
+
+ ret = wilc_parse_gpios(wilc);
+ if (ret < 0)
+ goto netdev_cleanup;
+
+ wilc->rtc_clk = devm_clk_get_optional_enabled(&spi->dev, "rtc");
+ if (IS_ERR(wilc->rtc_clk)) {
+ ret = PTR_ERR(wilc->rtc_clk);
+ goto netdev_cleanup;
+ }
+
+ dev_info(&spi->dev, "Selected CRC config: crc7=%s, crc16=%s\n",
+ enable_crc7 ? "on" : "off", enable_crc16 ? "on" : "off");
+
+ /* we need power to configure the bus protocol and to read the chip id: */
+
+ wilc_wlan_power(wilc, true);
+
+ ret = wilc_spi_configure_bus_protocol(wilc);
+ if (ret)
+ goto power_down;
+
+ ret = wilc_get_chipid(wilc);
+ if (ret)
+ goto power_down;
+
+ ret = wilc_cfg80211_register(wilc);
+ if (ret)
+ goto power_down;
+
+ ret = wilc_load_mac_from_nv(wilc);
+ if (ret) {
+ pr_err("Can not retrieve MAC address from chip\n");
+ goto unregister_wiphy;
+ }
+
+ wilc_wlan_power(wilc, false);
+ vif = wilc_netdev_ifc_init(wilc, "wlan%d", WILC_STATION_MODE,
+ NL80211_IFTYPE_STATION, false);
+ if (IS_ERR(vif)) {
+ ret = PTR_ERR(vif);
+ goto unregister_wiphy;
+ }
+ return 0;
+
+unregister_wiphy:
+ wiphy_unregister(wilc->wiphy);
+power_down:
+ wilc_wlan_power(wilc, false);
+netdev_cleanup:
+ wilc_netdev_cleanup(wilc);
+ wiphy_free(wilc->wiphy);
+free:
+ kfree(spi_priv);
+ return ret;
+}
+
+static void wilc_bus_remove(struct spi_device *spi)
+{
+ struct wilc *wilc = spi_get_drvdata(spi);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+
+ wilc_netdev_cleanup(wilc);
+ wiphy_unregister(wilc->wiphy);
+ wiphy_free(wilc->wiphy);
+ kfree(spi_priv);
+}
+
+static const struct of_device_id wilc_of_match[] = {
+ { .compatible = "microchip,wilc1000", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, wilc_of_match);
+
+static const struct spi_device_id wilc_spi_id[] = {
+ { "wilc1000", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, wilc_spi_id);
+
+static struct spi_driver wilc_spi_driver = {
+ .driver = {
+ .name = SPI_MODALIAS,
+ .of_match_table = wilc_of_match,
+ },
+ .id_table = wilc_spi_id,
+ .probe = wilc_bus_probe,
+ .remove = wilc_bus_remove,
+};
+module_spi_driver(wilc_spi_driver);
+MODULE_DESCRIPTION("Atmel WILC1000 SPI wireless driver");
+MODULE_LICENSE("GPL");
+
+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 = {
+ .value = 0,
+ .unit = SPI_DELAY_UNIT_USECS
+ },
+ };
+ 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);
+ 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 = {
+ .value = 0,
+ .unit = SPI_DELAY_UNIT_USECS
+ },
+
+ };
+ 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);
+ 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 = {
+ .value = 0,
+ .unit = SPI_DELAY_UNIT_USECS
+ },
+
+ };
+
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ 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_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 = 0;
+ u8 cmd, order, crc[2];
+ u16 crc_calc;
+
+ /*
+ * 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 = -EINVAL;
+ break;
+ }
+
+ /*
+ * Write data
+ */
+ if (wilc_spi_tx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed data block write, bus error...\n");
+ result = -EINVAL;
+ break;
+ }
+
+ /*
+ * Write CRC
+ */
+ if (spi_priv->crc16_enabled) {
+ crc_calc = crc_itu_t(0xffff, &b[ix], nbytes);
+ crc[0] = crc_calc >> 8;
+ crc[1] = crc_calc;
+ if (wilc_spi_tx(wilc, crc, 2)) {
+ dev_err(&spi->dev, "Failed data block crc write, bus error...\n");
+ result = -EINVAL;
+ break;
+ }
+ }
+
+ /*
+ * No need to wait for response
+ */
+ ix += nbytes;
+ sz -= nbytes;
+ } while (sz);
+
+ return result;
+}
+
+/********************************************
+ *
+ * Spi Internal Read/Write Function
+ *
+ ********************************************/
+static u8 wilc_get_crc7(u8 *buffer, u32 len)
+{
+ return crc7_be(0xfe, buffer, len) | 0x01;
+}
+
+static int wilc_spi_single_read(struct wilc *wilc, u8 cmd, u32 adr, void *b,
+ u8 clockless)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u8 wb[32], rb[32];
+ int cmd_len, resp_len, i;
+ u16 crc_calc, crc_recv;
+ struct wilc_spi_cmd *c;
+ struct wilc_spi_rsp_data *r;
+ struct wilc_spi_read_rsp_data *r_data;
+
+ memset(wb, 0x0, sizeof(wb));
+ memset(rb, 0x0, sizeof(rb));
+ c = (struct wilc_spi_cmd *)wb;
+ c->cmd_type = cmd;
+ if (cmd == CMD_SINGLE_READ) {
+ c->u.simple_cmd.addr[0] = adr >> 16;
+ c->u.simple_cmd.addr[1] = adr >> 8;
+ c->u.simple_cmd.addr[2] = adr;
+ } else if (cmd == CMD_INTERNAL_READ) {
+ c->u.simple_cmd.addr[0] = adr >> 8;
+ if (clockless == 1)
+ c->u.simple_cmd.addr[0] |= BIT(7);
+ c->u.simple_cmd.addr[1] = adr;
+ c->u.simple_cmd.addr[2] = 0x0;
+ } else {
+ dev_err(&spi->dev, "cmd [%x] not supported\n", cmd);
+ return -EINVAL;
+ }
+
+ cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc);
+ resp_len = sizeof(*r) + sizeof(*r_data) + WILC_SPI_RSP_HDR_EXTRA_DATA;
+
+ if (spi_priv->crc7_enabled) {
+ c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
+ cmd_len += 1;
+ resp_len += 2;
+ }
+
+ if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
+ dev_err(&spi->dev,
+ "spi buffer size too small (%d) (%d) (%zu)\n",
+ cmd_len, resp_len, ARRAY_SIZE(wb));
+ return -EINVAL;
+ }
+
+ if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
+ dev_err(&spi->dev, "Failed cmd write, bus error...\n");
+ return -EINVAL;
+ }
+
+ r = (struct wilc_spi_rsp_data *)&rb[cmd_len];
+ if (r->rsp_cmd_type != cmd && !clockless) {
+ if (!spi_priv->probing_crc)
+ dev_err(&spi->dev,
+ "Failed cmd, cmd (%02x), resp (%02x)\n",
+ cmd, r->rsp_cmd_type);
+ return -EINVAL;
+ }
+
+ if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS && !clockless) {
+ dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+ r->status);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < WILC_SPI_RSP_HDR_EXTRA_DATA; ++i)
+ if (WILC_GET_RESP_HDR_START(r->data[i]) == 0xf)
+ break;
+
+ if (i >= WILC_SPI_RSP_HDR_EXTRA_DATA) {
+ dev_err(&spi->dev, "Error, data start missing\n");
+ return -EINVAL;
+ }
+
+ r_data = (struct wilc_spi_read_rsp_data *)&r->data[i];
+
+ if (b)
+ memcpy(b, r_data->data, 4);
+
+ if (!clockless && spi_priv->crc16_enabled) {
+ crc_recv = (r_data->crc[0] << 8) | r_data->crc[1];
+ crc_calc = crc_itu_t(0xffff, r_data->data, 4);
+ if (crc_recv != crc_calc) {
+ dev_err(&spi->dev, "%s: bad CRC 0x%04x "
+ "(calculated 0x%04x)\n", __func__,
+ crc_recv, crc_calc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int wilc_spi_write_cmd(struct wilc *wilc, u8 cmd, u32 adr, u32 data,
+ u8 clockless)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u8 wb[32], rb[32];
+ int cmd_len, resp_len;
+ struct wilc_spi_cmd *c;
+ struct wilc_spi_rsp_data *r;
+
+ memset(wb, 0x0, sizeof(wb));
+ memset(rb, 0x0, sizeof(rb));
+ c = (struct wilc_spi_cmd *)wb;
+ c->cmd_type = cmd;
+ if (cmd == CMD_INTERNAL_WRITE) {
+ c->u.internal_w_cmd.addr[0] = adr >> 8;
+ if (clockless == 1)
+ c->u.internal_w_cmd.addr[0] |= BIT(7);
+
+ c->u.internal_w_cmd.addr[1] = adr;
+ c->u.internal_w_cmd.data = cpu_to_be32(data);
+ cmd_len = offsetof(struct wilc_spi_cmd, u.internal_w_cmd.crc);
+ if (spi_priv->crc7_enabled)
+ c->u.internal_w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
+ } else if (cmd == CMD_SINGLE_WRITE) {
+ c->u.w_cmd.addr[0] = adr >> 16;
+ c->u.w_cmd.addr[1] = adr >> 8;
+ c->u.w_cmd.addr[2] = adr;
+ c->u.w_cmd.data = cpu_to_be32(data);
+ cmd_len = offsetof(struct wilc_spi_cmd, u.w_cmd.crc);
+ if (spi_priv->crc7_enabled)
+ c->u.w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
+ } else {
+ dev_err(&spi->dev, "write cmd [%x] not supported\n", cmd);
+ return -EINVAL;
+ }
+
+ if (spi_priv->crc7_enabled)
+ cmd_len += 1;
+
+ resp_len = sizeof(*r);
+
+ if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
+ dev_err(&spi->dev,
+ "spi buffer size too small (%d) (%d) (%zu)\n",
+ cmd_len, resp_len, ARRAY_SIZE(wb));
+ return -EINVAL;
+ }
+
+ if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
+ dev_err(&spi->dev, "Failed cmd write, bus error...\n");
+ return -EINVAL;
+ }
+
+ r = (struct wilc_spi_rsp_data *)&rb[cmd_len];
+ /*
+ * Clockless registers operations might return unexptected responses,
+ * even if successful.
+ */
+ if (r->rsp_cmd_type != cmd && !clockless) {
+ dev_err(&spi->dev,
+ "Failed cmd response, cmd (%02x), resp (%02x)\n",
+ cmd, r->rsp_cmd_type);
+ return -EINVAL;
+ }
+
+ if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS && !clockless) {
+ dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+ r->status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wilc_spi_dma_rw(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u16 crc_recv, crc_calc;
+ u8 wb[32], rb[32];
+ int cmd_len, resp_len;
+ int retry, ix = 0;
+ u8 crc[2];
+ struct wilc_spi_cmd *c;
+ struct wilc_spi_rsp_data *r;
+
+ memset(wb, 0x0, sizeof(wb));
+ memset(rb, 0x0, sizeof(rb));
+ c = (struct wilc_spi_cmd *)wb;
+ c->cmd_type = cmd;
+ if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_READ) {
+ c->u.dma_cmd.addr[0] = adr >> 16;
+ c->u.dma_cmd.addr[1] = adr >> 8;
+ c->u.dma_cmd.addr[2] = adr;
+ c->u.dma_cmd.size[0] = sz >> 8;
+ c->u.dma_cmd.size[1] = sz;
+ cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd.crc);
+ if (spi_priv->crc7_enabled)
+ c->u.dma_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
+ } else if (cmd == CMD_DMA_EXT_WRITE || cmd == CMD_DMA_EXT_READ) {
+ c->u.dma_cmd_ext.addr[0] = adr >> 16;
+ c->u.dma_cmd_ext.addr[1] = adr >> 8;
+ c->u.dma_cmd_ext.addr[2] = adr;
+ c->u.dma_cmd_ext.size[0] = sz >> 16;
+ c->u.dma_cmd_ext.size[1] = sz >> 8;
+ c->u.dma_cmd_ext.size[2] = sz;
+ cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd_ext.crc);
+ if (spi_priv->crc7_enabled)
+ c->u.dma_cmd_ext.crc[0] = wilc_get_crc7(wb, cmd_len);
+ } else {
+ dev_err(&spi->dev, "dma read write cmd [%x] not supported\n",
+ cmd);
+ return -EINVAL;
+ }
+ if (spi_priv->crc7_enabled)
+ cmd_len += 1;
+
+ resp_len = sizeof(*r);
+
+ if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
+ dev_err(&spi->dev, "spi buffer size too small (%d)(%d) (%zu)\n",
+ cmd_len, resp_len, ARRAY_SIZE(wb));
+ return -EINVAL;
+ }
+
+ if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
+ dev_err(&spi->dev, "Failed cmd write, bus error...\n");
+ return -EINVAL;
+ }
+
+ r = (struct wilc_spi_rsp_data *)&rb[cmd_len];
+ if (r->rsp_cmd_type != cmd) {
+ dev_err(&spi->dev,
+ "Failed cmd response, cmd (%02x), resp (%02x)\n",
+ cmd, r->rsp_cmd_type);
+ return -EINVAL;
+ }
+
+ if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) {
+ dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+ r->status);
+ return -EINVAL;
+ }
+
+ if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_EXT_WRITE)
+ return 0;
+
+ while (sz > 0) {
+ int nbytes;
+ u8 rsp;
+
+ nbytes = min_t(u32, sz, DATA_PKT_SZ);
+
+ /*
+ * Data Response header
+ */
+ retry = 100;
+ do {
+ if (wilc_spi_rx(wilc, &rsp, 1)) {
+ dev_err(&spi->dev,
+ "Failed resp read, bus err\n");
+ return -EINVAL;
+ }
+ if (WILC_GET_RESP_HDR_START(rsp) == 0xf)
+ break;
+ } while (retry--);
+
+ /*
+ * Read bytes
+ */
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed block read, bus err\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Read CRC
+ */
+ if (spi_priv->crc16_enabled) {
+ if (wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev,
+ "Failed block CRC read, bus err\n");
+ return -EINVAL;
+ }
+ crc_recv = (crc[0] << 8) | crc[1];
+ crc_calc = crc_itu_t(0xffff, &b[ix], nbytes);
+ if (crc_recv != crc_calc) {
+ dev_err(&spi->dev, "%s: bad CRC 0x%04x "
+ "(calculated 0x%04x)\n", __func__,
+ crc_recv, crc_calc);
+ return -EINVAL;
+ }
+ }
+
+ ix += nbytes;
+ sz -= nbytes;
+ }
+ return 0;
+}
+
+static int wilc_spi_special_cmd(struct wilc *wilc, u8 cmd)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u8 wb[32], rb[32];
+ int cmd_len, resp_len = 0;
+ struct wilc_spi_cmd *c;
+ struct wilc_spi_special_cmd_rsp *r;
+
+ if (cmd != CMD_TERMINATE && cmd != CMD_REPEAT && cmd != CMD_RESET)
+ return -EINVAL;
+
+ memset(wb, 0x0, sizeof(wb));
+ memset(rb, 0x0, sizeof(rb));
+ c = (struct wilc_spi_cmd *)wb;
+ c->cmd_type = cmd;
+
+ if (cmd == CMD_RESET)
+ memset(c->u.simple_cmd.addr, 0xFF, 3);
+
+ cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc);
+ resp_len = sizeof(*r);
+
+ if (spi_priv->crc7_enabled) {
+ c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len);
+ cmd_len += 1;
+ }
+ if (cmd_len + resp_len > ARRAY_SIZE(wb)) {
+ dev_err(&spi->dev, "spi buffer size too small (%d) (%d) (%zu)\n",
+ cmd_len, resp_len, ARRAY_SIZE(wb));
+ return -EINVAL;
+ }
+
+ if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) {
+ dev_err(&spi->dev, "Failed cmd write, bus error...\n");
+ return -EINVAL;
+ }
+
+ r = (struct wilc_spi_special_cmd_rsp *)&rb[cmd_len];
+ if (r->rsp_cmd_type != cmd) {
+ if (!spi_priv->probing_crc)
+ dev_err(&spi->dev,
+ "Failed cmd response, cmd (%02x), resp (%02x)\n",
+ cmd, r->rsp_cmd_type);
+ return -EINVAL;
+ }
+
+ if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) {
+ dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+ r->status);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void wilc_spi_reset_cmd_sequence(struct wilc *wl, u8 attempt, u32 addr)
+{
+ struct spi_device *spi = to_spi_device(wl->dev);
+ struct wilc_spi *spi_priv = wl->bus_data;
+
+ if (!spi_priv->probing_crc)
+ dev_err(&spi->dev, "Reset and retry %d %x\n", attempt, addr);
+
+ usleep_range(1000, 1100);
+ wilc_spi_reset(wl);
+ usleep_range(1000, 1100);
+}
+
+static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result;
+ u8 cmd = CMD_SINGLE_READ;
+ u8 clockless = 0;
+ u8 i;
+
+ if (addr <= WILC_SPI_CLOCKLESS_ADDR_LIMIT) {
+ /* Clockless register */
+ cmd = CMD_INTERNAL_READ;
+ clockless = 1;
+ }
+
+ for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
+ result = wilc_spi_single_read(wilc, cmd, addr, data, clockless);
+ if (!result) {
+ le32_to_cpus(data);
+ return 0;
+ }
+
+ /* retry is not applicable for clockless registers */
+ if (clockless)
+ break;
+
+ dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr);
+ wilc_spi_reset_cmd_sequence(wilc, i, addr);
+ }
+
+ 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 i;
+
+ if (size <= 4)
+ return -EINVAL;
+
+ for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
+ result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_READ, addr,
+ buf, size);
+ if (!result)
+ return 0;
+
+ dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr);
+
+ wilc_spi_reset_cmd_sequence(wilc, i, addr);
+ }
+
+ return result;
+}
+
+static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result;
+ u8 i;
+
+ for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
+ result = wilc_spi_write_cmd(wilc, CMD_INTERNAL_WRITE, adr,
+ dat, 0);
+ if (!result)
+ return 0;
+ dev_err(&spi->dev, "Failed internal write cmd...\n");
+
+ wilc_spi_reset_cmd_sequence(wilc, i, adr);
+ }
+
+ return result;
+}
+
+static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ int result;
+ u8 i;
+
+ for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
+ result = wilc_spi_single_read(wilc, CMD_INTERNAL_READ, adr,
+ data, 0);
+ if (!result) {
+ le32_to_cpus(data);
+ return 0;
+ }
+ if (!spi_priv->probing_crc)
+ dev_err(&spi->dev, "Failed internal read cmd...\n");
+
+ wilc_spi_reset_cmd_sequence(wilc, i, adr);
+ }
+
+ 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);
+ int result;
+ u8 cmd = CMD_SINGLE_WRITE;
+ u8 clockless = 0;
+ u8 i;
+
+ if (addr <= WILC_SPI_CLOCKLESS_ADDR_LIMIT) {
+ /* Clockless register */
+ cmd = CMD_INTERNAL_WRITE;
+ clockless = 1;
+ }
+
+ for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
+ result = wilc_spi_write_cmd(wilc, cmd, addr, data, clockless);
+ if (!result)
+ return 0;
+
+ dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr);
+
+ if (clockless)
+ break;
+
+ wilc_spi_reset_cmd_sequence(wilc, i, addr);
+ }
+ return result;
+}
+
+static int spi_data_rsp(struct wilc *wilc, u8 cmd)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int result, i;
+ u8 rsp[4];
+
+ /*
+ * The response to data packets is two bytes long. For
+ * efficiency's sake, wilc_spi_write() wisely ignores the
+ * responses for all packets but the final one. The downside
+ * of that optimization is that when the final data packet is
+ * short, we may receive (part of) the response to the
+ * second-to-last packet before the one for the final packet.
+ * To handle this, we always read 4 bytes and then search for
+ * the last byte that contains the "Response Start" code (0xc
+ * in the top 4 bits). We then know that this byte is the
+ * first response byte of the final data packet.
+ */
+ result = wilc_spi_rx(wilc, rsp, sizeof(rsp));
+ if (result) {
+ dev_err(&spi->dev, "Failed bus error...\n");
+ return result;
+ }
+
+ for (i = sizeof(rsp) - 2; i >= 0; --i)
+ if (FIELD_GET(RSP_START_FIELD, rsp[i]) == RSP_START_TAG)
+ break;
+
+ if (i < 0) {
+ dev_err(&spi->dev,
+ "Data packet response missing (%02x %02x %02x %02x)\n",
+ rsp[0], rsp[1], rsp[2], rsp[3]);
+ return -1;
+ }
+
+ /* rsp[i] is the last response start byte */
+
+ if (FIELD_GET(RSP_TYPE_FIELD, rsp[i]) != RSP_TYPE_LAST_PACKET
+ || rsp[i + 1] != RSP_STATE_NO_ERROR) {
+ dev_err(&spi->dev, "Data response error (%02x %02x)\n",
+ rsp[i], rsp[i + 1]);
+ return -1;
+ }
+ return 0;
+}
+
+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 i;
+
+ /*
+ * has to be greated than 4
+ */
+ if (size <= 4)
+ return -EINVAL;
+
+ for (i = 0; i < SPI_RETRY_MAX_LIMIT; i++) {
+ result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_WRITE, addr,
+ NULL, size);
+ if (result) {
+ dev_err(&spi->dev,
+ "Failed cmd, write block (%08x)...\n", addr);
+ wilc_spi_reset_cmd_sequence(wilc, i, addr);
+ continue;
+ }
+
+ /*
+ * Data
+ */
+ result = spi_data_write(wilc, buf, size);
+ if (result) {
+ dev_err(&spi->dev, "Failed block data write...\n");
+ wilc_spi_reset_cmd_sequence(wilc, i, addr);
+ continue;
+ }
+
+ /*
+ * Data response
+ */
+ result = spi_data_rsp(wilc, CMD_DMA_EXT_WRITE);
+ if (result) {
+ dev_err(&spi->dev, "Failed block data rsp...\n");
+ wilc_spi_reset_cmd_sequence(wilc, i, addr);
+ continue;
+ }
+ break;
+ }
+ return result;
+}
+
+/********************************************
+ *
+ * Bus interfaces
+ *
+ ********************************************/
+
+static int wilc_spi_reset(struct wilc *wilc)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ int result;
+
+ result = wilc_spi_special_cmd(wilc, CMD_RESET);
+ if (result && !spi_priv->probing_crc)
+ dev_err(&spi->dev, "Failed cmd reset\n");
+
+ return result;
+}
+
+static bool wilc_spi_is_init(struct wilc *wilc)
+{
+ struct wilc_spi *spi_priv = wilc->bus_data;
+
+ return spi_priv->isinit;
+}
+
+static int wilc_spi_deinit(struct wilc *wilc)
+{
+ struct wilc_spi *spi_priv = wilc->bus_data;
+
+ spi_priv->isinit = false;
+ wilc_wlan_power(wilc, false);
+ return 0;
+}
+
+static int wilc_spi_init(struct wilc *wilc, bool resume)
+{
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ int ret;
+
+ if (spi_priv->isinit) {
+ /* Confirm we can read chipid register without error: */
+ if (wilc_validate_chipid(wilc) == 0)
+ return 0;
+ }
+
+ wilc_wlan_power(wilc, true);
+
+ ret = wilc_spi_configure_bus_protocol(wilc);
+ if (ret) {
+ wilc_wlan_power(wilc, false);
+ return ret;
+ }
+
+ spi_priv->isinit = true;
+
+ return 0;
+}
+
+static int wilc_spi_configure_bus_protocol(struct wilc *wilc)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ struct wilc_spi *spi_priv = wilc->bus_data;
+ u32 reg;
+ int ret, i;
+
+ /*
+ * Infer the CRC settings that are currently in effect. This
+ * is necessary because we can't be sure that the chip has
+ * been RESET (e.g, after module unload and reload).
+ */
+ spi_priv->probing_crc = true;
+ spi_priv->crc7_enabled = enable_crc7;
+ spi_priv->crc16_enabled = false; /* don't check CRC16 during probing */
+ for (i = 0; i < 2; ++i) {
+ ret = spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg);
+ if (ret == 0)
+ break;
+ spi_priv->crc7_enabled = !enable_crc7;
+ }
+ if (ret) {
+ dev_err(&spi->dev, "Failed with CRC7 on and off.\n");
+ return ret;
+ }
+
+ /* set up the desired CRC configuration: */
+ reg &= ~(PROTOCOL_REG_CRC7_MASK | PROTOCOL_REG_CRC16_MASK);
+ if (enable_crc7)
+ reg |= PROTOCOL_REG_CRC7_MASK;
+ if (enable_crc16)
+ reg |= PROTOCOL_REG_CRC16_MASK;
+
+ /* set up the data packet size: */
+ BUILD_BUG_ON(DATA_PKT_LOG_SZ < DATA_PKT_LOG_SZ_MIN
+ || DATA_PKT_LOG_SZ > DATA_PKT_LOG_SZ_MAX);
+ reg &= ~PROTOCOL_REG_PKT_SZ_MASK;
+ reg |= FIELD_PREP(PROTOCOL_REG_PKT_SZ_MASK,
+ DATA_PKT_LOG_SZ - DATA_PKT_LOG_SZ_MIN);
+
+ /* establish the new setup: */
+ ret = spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg);
+ if (ret) {
+ dev_err(&spi->dev,
+ "[wilc spi %d]: Failed internal write reg\n",
+ __LINE__);
+ return ret;
+ }
+ /* update our state to match new protocol settings: */
+ spi_priv->crc7_enabled = enable_crc7;
+ spi_priv->crc16_enabled = enable_crc16;
+
+ /* re-read to make sure new settings are in effect: */
+ spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg);
+
+ spi_priv->probing_crc = false;
+
+ return 0;
+}
+
+static int wilc_validate_chipid(struct wilc *wilc)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ u32 chipid;
+ int ret;
+
+ /*
+ * make sure can read chip id without protocol error
+ */
+ ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid);
+ if (ret) {
+ dev_err(&spi->dev, "Fail cmd read chip id...\n");
+ return ret;
+ }
+ if (!is_wilc1000(chipid) && !is_wilc3000(chipid)) {
+ dev_err(&spi->dev, "Unknown chip id 0x%x\n", chipid);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int wilc_spi_read_size(struct wilc *wilc, u32 *size)
+{
+ int ret;
+
+ ret = spi_internal_read(wilc,
+ WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE, size);
+ *size = FIELD_GET(IRQ_DMA_WD_CNT_MASK, *size);
+
+ return ret;
+}
+
+static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status)
+{
+ return spi_internal_read(wilc, WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE,
+ int_status);
+}
+
+static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val)
+{
+ int ret;
+ int retry = SPI_ENABLE_VMM_RETRY_LIMIT;
+ u32 check;
+
+ while (retry) {
+ ret = spi_internal_write(wilc,
+ WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE,
+ val);
+ if (ret)
+ break;
+
+ ret = spi_internal_read(wilc,
+ WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE,
+ &check);
+ if (ret || ((check & EN_VMM) == (val & EN_VMM)))
+ break;
+
+ retry--;
+ }
+ return ret;
+}
+
+static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ u32 reg;
+ int ret, i;
+
+ if (nint > MAX_NUM_INT) {
+ dev_err(&spi->dev, "Too many interrupts (%d)...\n", nint);
+ return -EINVAL;
+ }
+
+ /*
+ * 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 ret;
+ }
+ 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 ret;
+ }
+
+ /*
+ * 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 ret;
+ }
+
+ 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 ret;
+ }
+ 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 ret;
+ }
+
+ 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 ret;
+ }
+ }
+
+ return 0;
+}
+
+/* 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/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c
new file mode 100644
index 000000000..4b116fe6f
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/wlan.c
@@ -0,0 +1,1810 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <net/dsfield.h>
+#include "cfg80211.h"
+#include "wlan_cfg.h"
+
+#define WAKE_UP_TRIAL_RETRY 10000
+
+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 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);
+ 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++;
+
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+
+ 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++;
+
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+ mutex_unlock(&wilc->txq_add_to_head_cs);
+ complete(&wilc->txq_event);
+}
+
+#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++;
+ }
+}
+
+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;
+
+ 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) {
+ wait_for_completion_timeout(&wilc->txq_event,
+ msecs_to_jiffies(1));
+ dropped--;
+ }
+}
+
+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;
+
+ netdev_dbg(vif->ndev, "Adding config packet ...\n");
+ if (wilc->quit) {
+ netdev_dbg(vif->ndev, "Return due to clear function\n");
+ complete(&wilc->cfg_event);
+ return 0;
+ }
+
+ tqe = kmalloc_obj(*tqe, GFP_ATOMIC);
+ 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;
+ tqe->vif = vif;
+
+ wilc_wlan_txq_add_to_head(vif, AC_VO_Q, tqe);
+
+ return 1;
+}
+
+static bool is_ac_q_limit(struct wilc *wl, u8 q_num)
+{
+ u8 factors[NQUEUES] = {1, 1, 1, 1};
+ u16 i;
+ unsigned long flags;
+ struct wilc_tx_queue_status *q = &wl->tx_q_limit;
+ u8 end_index;
+ u8 q_limit;
+ bool ret = false;
+
+ spin_lock_irqsave(&wl->txq_spinlock, flags);
+ if (!q->initialized) {
+ for (i = 0; i < AC_BUFFER_SIZE; i++)
+ q->buffer[i] = i % NQUEUES;
+
+ for (i = 0; i < NQUEUES; i++) {
+ q->cnt[i] = AC_BUFFER_SIZE * factors[i] / NQUEUES;
+ q->sum += q->cnt[i];
+ }
+ q->end_index = AC_BUFFER_SIZE - 1;
+ q->initialized = 1;
+ }
+
+ end_index = q->end_index;
+ q->cnt[q->buffer[end_index]] -= factors[q->buffer[end_index]];
+ q->cnt[q_num] += factors[q_num];
+ q->sum += (factors[q_num] - factors[q->buffer[end_index]]);
+
+ q->buffer[end_index] = q_num;
+ if (end_index > 0)
+ q->end_index--;
+ else
+ q->end_index = AC_BUFFER_SIZE - 1;
+
+ if (!q->sum)
+ q_limit = 1;
+ else
+ q_limit = (q->cnt[q_num] * FLOW_CONTROL_UPPER_THRESHOLD / q->sum) + 1;
+
+ if (wl->txq[q_num].count <= q_limit)
+ ret = true;
+
+ spin_unlock_irqrestore(&wl->txq_spinlock, flags);
+
+ return ret;
+}
+
+static inline u8 ac_classify(struct wilc *wilc, struct sk_buff *skb)
+{
+ u8 q_num = AC_BE_Q;
+ u8 dscp;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
+ break;
+ case htons(ETH_P_IPV6):
+ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;
+ break;
+ default:
+ return q_num;
+ }
+
+ switch (dscp) {
+ case 0x08:
+ case 0x20:
+ case 0x40:
+ q_num = AC_BK_Q;
+ break;
+ case 0x80:
+ case 0xA0:
+ case 0x28:
+ q_num = AC_VI_Q;
+ break;
+ case 0xC0:
+ case 0xD0:
+ case 0xE0:
+ case 0x88:
+ case 0xB8:
+ q_num = AC_VO_Q;
+ break;
+ }
+
+ return q_num;
+}
+
+static inline int ac_balance(struct wilc *wl, u8 *ratio)
+{
+ u8 i, max_count = 0;
+
+ if (!ratio)
+ return -EINVAL;
+
+ for (i = 0; i < NQUEUES; i++)
+ if (wl->txq[i].fw.count > max_count)
+ max_count = wl->txq[i].fw.count;
+
+ for (i = 0; i < NQUEUES; i++)
+ ratio[i] = max_count - wl->txq[i].fw.count;
+
+ return 0;
+}
+
+static inline void ac_update_fw_ac_pkt_info(struct wilc *wl, u32 reg)
+{
+ wl->txq[AC_BK_Q].fw.count = FIELD_GET(BK_AC_COUNT_FIELD, reg);
+ wl->txq[AC_BE_Q].fw.count = FIELD_GET(BE_AC_COUNT_FIELD, reg);
+ wl->txq[AC_VI_Q].fw.count = FIELD_GET(VI_AC_COUNT_FIELD, reg);
+ wl->txq[AC_VO_Q].fw.count = FIELD_GET(VO_AC_COUNT_FIELD, reg);
+
+ wl->txq[AC_BK_Q].fw.acm = FIELD_GET(BK_AC_ACM_STAT_FIELD, reg);
+ wl->txq[AC_BE_Q].fw.acm = FIELD_GET(BE_AC_ACM_STAT_FIELD, reg);
+ wl->txq[AC_VI_Q].fw.acm = FIELD_GET(VI_AC_ACM_STAT_FIELD, reg);
+ wl->txq[AC_VO_Q].fw.acm = FIELD_GET(VO_AC_ACM_STAT_FIELD, reg);
+}
+
+static inline u8 ac_change(struct wilc *wilc, u8 *ac)
+{
+ do {
+ if (wilc->txq[*ac].fw.acm == 0)
+ return 0;
+ (*ac)++;
+ } while (*ac < NQUEUES);
+
+ return 1;
+}
+
+int wilc_wlan_txq_add_net_pkt(struct net_device *dev,
+ struct tx_complete_data *tx_data, 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;
+
+ wilc = vif->wilc;
+
+ if (wilc->quit) {
+ tx_complete_fn(tx_data, 0);
+ return 0;
+ }
+
+ if (!wilc->initialized) {
+ tx_complete_fn(tx_data, 0);
+ return 0;
+ }
+
+ tqe = kmalloc_obj(*tqe, GFP_ATOMIC);
+
+ if (!tqe) {
+ tx_complete_fn(tx_data, 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 = tx_data;
+ tqe->vif = vif;
+
+ q_num = ac_classify(wilc, tx_data->skb);
+ tqe->q_num = q_num;
+ if (ac_change(wilc, &q_num)) {
+ tx_complete_fn(tx_data, 0);
+ kfree(tqe);
+ return 0;
+ }
+
+ if (is_ac_q_limit(wilc, q_num)) {
+ 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 {
+ tx_complete_fn(tx_data, 0);
+ kfree(tqe);
+ }
+
+ return wilc->txq_entries;
+}
+
+int wilc_wlan_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) {
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+
+ if (!wilc->initialized) {
+ tx_complete_fn(priv, 0);
+ return 0;
+ }
+ tqe = kmalloc_obj(*tqe, GFP_ATOMIC);
+
+ if (!tqe) {
+ 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;
+ tqe->vif = vif;
+ wilc_wlan_txq_add_to_tail(dev, AC_VO_Q, tqe);
+ return 1;
+}
+
+static struct txq_entry_t *wilc_wlan_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 *wilc_wlan_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 wilc_wlan_rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe)
+{
+ if (wilc->quit)
+ return;
+
+ mutex_lock(&wilc->rxq_cs);
+ list_add_tail(&rqe->list, &wilc->rxq_head.list);
+ mutex_unlock(&wilc->rxq_cs);
+}
+
+static struct rxq_entry_t *wilc_wlan_rxq_remove(struct wilc *wilc)
+{
+ struct rxq_entry_t *rqe = NULL;
+
+ 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);
+ }
+ mutex_unlock(&wilc->rxq_cs);
+ return rqe;
+}
+
+static int chip_allow_sleep_wilc1000(struct wilc *wilc)
+{
+ 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) {
+ wakeup_reg = WILC_SDIO_WAKEUP_REG;
+ wakeup_bit = WILC_SDIO_WAKEUP_BIT;
+ from_host_to_fw_reg = WILC_SDIO_HOST_TO_FW_REG;
+ from_host_to_fw_bit = WILC_SDIO_HOST_TO_FW_BIT;
+ to_host_from_fw_reg = WILC_SDIO_FW_TO_HOST_REG;
+ to_host_from_fw_bit = WILC_SDIO_FW_TO_HOST_BIT;
+ } else {
+ wakeup_reg = WILC_SPI_WAKEUP_REG;
+ wakeup_bit = WILC_SPI_WAKEUP_BIT;
+ from_host_to_fw_reg = WILC_SPI_HOST_TO_FW_REG;
+ from_host_to_fw_bit = WILC_SPI_HOST_TO_FW_BIT;
+ to_host_from_fw_reg = WILC_SPI_FW_TO_HOST_REG;
+ to_host_from_fw_bit = WILC_SPI_FW_TO_HOST_BIT;
+ }
+
+ while (--trials) {
+ ret = hif_func->hif_read_reg(wilc, to_host_from_fw_reg, &reg);
+ if (ret)
+ return ret;
+ 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 ret;
+ if (reg & wakeup_bit) {
+ reg &= ~wakeup_bit;
+ ret = hif_func->hif_write_reg(wilc, wakeup_reg, reg);
+ if (ret)
+ return ret;
+ }
+
+ ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, &reg);
+ if (ret)
+ return ret;
+ 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 ret;
+ }
+
+ return 0;
+}
+
+static int chip_allow_sleep_wilc3000(struct wilc *wilc)
+{
+ u32 reg = 0;
+ int ret;
+ const struct wilc_hif_func *hif_func = wilc->hif_func;
+
+ if (wilc->io_type == WILC_HIF_SDIO) {
+ ret = hif_func->hif_read_reg(wilc, WILC_SDIO_WAKEUP_REG, &reg);
+ if (ret)
+ return ret;
+ ret = hif_func->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG,
+ reg & ~WILC_SDIO_WAKEUP_BIT);
+ if (ret)
+ return ret;
+ } else {
+ ret = hif_func->hif_read_reg(wilc, WILC_SPI_WAKEUP_REG, &reg);
+ if (ret)
+ return ret;
+ ret = hif_func->hif_write_reg(wilc, WILC_SPI_WAKEUP_REG,
+ reg & ~WILC_SPI_WAKEUP_BIT);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int chip_allow_sleep(struct wilc *wilc)
+{
+ if (is_wilc1000(wilc->chipid))
+ return chip_allow_sleep_wilc1000(wilc);
+ else
+ return chip_allow_sleep_wilc3000(wilc);
+}
+
+static int chip_wakeup_wilc1000(struct wilc *wilc)
+{
+ u32 ret = 0;
+ u32 clk_status_val = 0, trials = 0;
+ u32 wakeup_reg, wakeup_bit;
+ u32 clk_status_reg, clk_status_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) {
+ wakeup_reg = WILC_SDIO_WAKEUP_REG;
+ wakeup_bit = WILC_SDIO_WAKEUP_BIT;
+ clk_status_reg = WILC1000_SDIO_CLK_STATUS_REG;
+ clk_status_bit = WILC1000_SDIO_CLK_STATUS_BIT;
+ from_host_to_fw_reg = WILC_SDIO_HOST_TO_FW_REG;
+ from_host_to_fw_bit = WILC_SDIO_HOST_TO_FW_BIT;
+ } else {
+ wakeup_reg = WILC_SPI_WAKEUP_REG;
+ wakeup_bit = WILC_SPI_WAKEUP_BIT;
+ clk_status_reg = WILC1000_SPI_CLK_STATUS_REG;
+ clk_status_bit = WILC1000_SPI_CLK_STATUS_BIT;
+ from_host_to_fw_reg = WILC_SPI_HOST_TO_FW_REG;
+ from_host_to_fw_bit = WILC_SPI_HOST_TO_FW_BIT;
+ }
+
+ /* indicate host wakeup */
+ ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg,
+ from_host_to_fw_bit);
+ if (ret)
+ return ret;
+
+ /* Set wake-up bit */
+ ret = hif_func->hif_write_reg(wilc, wakeup_reg,
+ wakeup_bit);
+ if (ret)
+ return ret;
+
+ while (trials < WAKE_UP_TRIAL_RETRY) {
+ ret = hif_func->hif_read_reg(wilc, clk_status_reg,
+ &clk_status_val);
+ if (ret) {
+ pr_err("Bus error %d %x\n", ret, clk_status_val);
+ return ret;
+ }
+ if (clk_status_val & clk_status_bit)
+ break;
+
+ trials++;
+ }
+ if (trials >= WAKE_UP_TRIAL_RETRY) {
+ pr_err("Failed to wake-up the chip\n");
+ return -ETIMEDOUT;
+ }
+ /* Sometimes spi fail to read clock regs after reading
+ * writing clockless registers
+ */
+ if (wilc->io_type == WILC_HIF_SPI)
+ wilc->hif_func->hif_reset(wilc);
+
+ return 0;
+}
+
+static int chip_wakeup_wilc3000(struct wilc *wilc)
+{
+ 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) {
+ wakeup_reg = WILC_SDIO_WAKEUP_REG;
+ wakeup_bit = WILC_SDIO_WAKEUP_BIT;
+ clk_status_reg = WILC3000_SDIO_CLK_STATUS_REG;
+ clk_status_bit = WILC3000_SDIO_CLK_STATUS_BIT;
+ } else {
+ wakeup_reg = WILC_SPI_WAKEUP_REG;
+ wakeup_bit = WILC_SPI_WAKEUP_BIT;
+ clk_status_reg = WILC3000_SPI_CLK_STATUS_REG;
+ clk_status_bit = WILC3000_SPI_CLK_STATUS_BIT;
+ }
+
+ 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));
+ /* added wait before wakeup sequence retry */
+ usleep_range(200, 300);
+ }
+ } 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");
+
+ return 0;
+}
+
+static int chip_wakeup(struct wilc *wilc)
+{
+ if (is_wilc1000(wilc->chipid))
+ return chip_wakeup_wilc1000(wilc);
+ else
+ return chip_wakeup_wilc3000(wilc);
+}
+
+static inline int acquire_bus(struct wilc *wilc, enum bus_acquire acquire)
+{
+ int ret = 0;
+
+ mutex_lock(&wilc->hif_cs);
+ if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP && wilc->power_save_mode) {
+ ret = chip_wakeup(wilc);
+ if (ret)
+ mutex_unlock(&wilc->hif_cs);
+ }
+
+ return ret;
+}
+
+static inline int release_bus(struct wilc *wilc, enum bus_release release)
+{
+ int ret = 0;
+
+ if (release == WILC_BUS_RELEASE_ALLOW_SLEEP && wilc->power_save_mode)
+ ret = chip_allow_sleep(wilc);
+ mutex_unlock(&wilc->hif_cs);
+
+ return ret;
+}
+
+int host_wakeup_notify(struct wilc *wilc)
+{
+ int ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+
+ if (ret)
+ return ret;
+
+ wilc->hif_func->hif_write_reg(wilc, is_wilc1000(wilc->chipid) ?
+ WILC1000_CORTUS_INTERRUPT_2 :
+ WILC3000_CORTUS_INTERRUPT_2, 1);
+ return release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+}
+EXPORT_SYMBOL_GPL(host_wakeup_notify);
+
+int host_sleep_notify(struct wilc *wilc)
+{
+ int ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+
+ if (ret)
+ return ret;
+
+ wilc->hif_func->hif_write_reg(wilc, is_wilc1000(wilc->chipid) ?
+ WILC1000_CORTUS_INTERRUPT_1 :
+ WILC3000_CORTUS_INTERRUPT_1, 1);
+ return release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+}
+EXPORT_SYMBOL_GPL(host_sleep_notify);
+
+int wilc_wlan_handle_txq(struct wilc *wilc, 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];
+ 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_table;
+ u8 ac_pkt_num_to_chip[NQUEUES] = {0, 0, 0, 0};
+ const struct wilc_hif_func *func;
+ int srcu_idx;
+ u8 *txb = wilc->tx_buffer;
+ struct wilc_vif *vif;
+ int rv;
+
+ if (wilc->quit)
+ goto out_update_cnt;
+
+ if (ac_balance(wilc, ac_desired_ratio))
+ return -EINVAL;
+
+ mutex_lock(&wilc->txq_add_to_head_cs);
+
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ wilc_for_each_vif(wilc, vif)
+ wilc_wlan_txq_filter_dup_tcp_ack(vif->ndev);
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
+
+ for (ac = 0; ac < NQUEUES; ac++)
+ tqe_q[ac] = wilc_wlan_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;
+ vmm_sz = ALIGN(vmm_sz, 4);
+
+ if ((sum + vmm_sz) > WILC_TX_BUFF_SIZE) {
+ max_size_over = 1;
+ break;
+ }
+ vmm_table[i] = vmm_sz / 4;
+ if (tqe_q[ac]->type == WILC_CFG_PKT)
+ vmm_table[i] |= BIT(10);
+
+ cpu_to_le32s(&vmm_table[i]);
+ vmm_entries_ac[i] = ac;
+
+ i++;
+ sum += vmm_sz;
+ tqe_q[ac] = wilc_wlan_txq_get_next(wilc,
+ tqe_q[ac],
+ ac);
+ }
+ }
+ num_pkts_to_add = ac_preserve_ratio;
+ } while (!max_size_over && ac_exist);
+
+ if (i == 0)
+ goto out_unlock;
+ vmm_table[i] = 0x0;
+
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ goto out_unlock;
+
+ counter = 0;
+ func = wilc->hif_func;
+ do {
+ ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
+ if (ret)
+ break;
+
+ if ((reg & 0x1) == 0) {
+ ac_update_fw_ac_pkt_info(wilc, reg);
+ break;
+ }
+
+ counter++;
+ if (counter > 200) {
+ counter = 0;
+ 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,
+ WILC_VMM_TBL_RX_SHADOW_BASE,
+ (u8 *)vmm_table,
+ ((i + 1) * 4));
+ if (ret)
+ break;
+
+ if (is_wilc1000(wilc->chipid)) {
+ ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x2);
+ if (ret)
+ break;
+
+ do {
+ ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, &reg);
+ if (ret)
+ break;
+ if (FIELD_GET(WILC_VMM_ENTRY_AVAILABLE, reg)) {
+ entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg);
+ break;
+ }
+ } while (--timeout);
+ } else {
+ ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0);
+ if (ret)
+ break;
+
+ /* interrupt firmware */
+ ret = func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_BASE, 1);
+ if (ret)
+ break;
+
+ do {
+ ret = func->hif_read_reg(wilc, WILC_CORTUS_INTERRUPT_BASE, &reg);
+ if (ret)
+ break;
+ if (reg == 0) {
+ /* Get the entries */
+ ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, &reg);
+ if (ret)
+ break;
+
+ entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg);
+ break;
+ }
+ } while (--timeout);
+ }
+ if (timeout <= 0) {
+ ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
+ break;
+ }
+
+ if (ret)
+ break;
+
+ if (entries == 0) {
+ ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
+ if (ret)
+ break;
+ reg &= ~BIT(0);
+ ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
+ }
+ } while (0);
+
+ if (ret)
+ goto out_release_bus;
+
+ if (entries == 0) {
+ /*
+ * No VMM space available in firmware so retry to transmit
+ * the packet from tx queue.
+ */
+ ret = WILC_VMM_ENTRY_FULL_RETRY;
+ goto out_release_bus;
+ }
+
+ ret = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+ if (ret)
+ goto out_unlock;
+
+ offset = 0;
+ i = 0;
+ do {
+ struct txq_entry_t *tqe;
+ u32 header, buffer_offset;
+ char *bssid;
+ u8 mgmt_ptk = 0;
+
+ if (vmm_table[i] == 0 || vmm_entries_ac[i] >= NQUEUES)
+ break;
+
+ tqe = wilc_wlan_txq_remove_from_head(wilc, vmm_entries_ac[i]);
+ if (!tqe)
+ break;
+
+ ac_pkt_num_to_chip[vmm_entries_ac[i]]++;
+ vif = tqe->vif;
+
+ le32_to_cpus(&vmm_table[i]);
+ vmm_sz = FIELD_GET(WILC_VMM_BUFFER_SIZE, vmm_table[i]);
+ vmm_sz *= 4;
+
+ if (tqe->type == WILC_MGMT_PKT)
+ mgmt_ptk = 1;
+
+ header = (FIELD_PREP(WILC_VMM_HDR_TYPE, tqe->type) |
+ FIELD_PREP(WILC_VMM_HDR_MGMT_FIELD, mgmt_ptk) |
+ FIELD_PREP(WILC_VMM_HDR_PKT_SIZE, tqe->buffer_size) |
+ FIELD_PREP(WILC_VMM_HDR_BUFF_SIZE, vmm_sz));
+
+ 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) {
+ int prio = tqe->q_num;
+
+ bssid = tqe->vif->bssid;
+ 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++)
+ wilc->txq[i].fw.count += ac_pkt_num_to_chip[i];
+
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ goto out_unlock;
+
+ ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
+ if (ret)
+ goto out_release_bus;
+
+ ret = func->hif_block_tx_ext(wilc, 0, txb, offset);
+
+out_release_bus:
+ rv = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+ if (!ret && rv)
+ ret = rv;
+
+out_unlock:
+ mutex_unlock(&wilc->txq_add_to_head_cs);
+
+out_update_cnt:
+ *txq_count = wilc->txq_entries;
+ 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;
+
+ do {
+ buff_ptr = buffer + offset;
+ header = get_unaligned_le32(buff_ptr);
+
+ is_cfg_packet = FIELD_GET(WILC_PKT_HDR_CONFIG_FIELD, header);
+ pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header);
+ tp_len = FIELD_GET(WILC_PKT_HDR_TOTAL_LEN_FIELD, header);
+ pkt_len = FIELD_GET(WILC_PKT_HDR_LEN_FIELD, header);
+
+ if (pkt_len == 0 || tp_len == 0)
+ break;
+
+ if (pkt_offset & IS_MANAGMEMENT) {
+ buff_ptr += HOST_HDR_OFFSET;
+ wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len,
+ pkt_offset & IS_MGMT_AUTH_PKT);
+ } else {
+ if (!is_cfg_packet) {
+ wilc_frmw_to_host(wilc, buff_ptr, pkt_len,
+ pkt_offset);
+ } else {
+ struct wilc_cfg_rsp rsp;
+
+ buff_ptr += pkt_offset;
+
+ wilc_wlan_cfg_indicate_rx(wilc, buff_ptr,
+ pkt_len,
+ &rsp);
+ if (rsp.type == WILC_CFG_RSP) {
+ if (wilc->cfg_seq_no == rsp.seq_no)
+ complete(&wilc->cfg_event);
+ } else if (rsp.type == WILC_CFG_RSP_STATUS) {
+ wilc_mac_indicate(wilc);
+ }
+ }
+ }
+ offset += tp_len;
+ } while (offset < size);
+}
+
+static void wilc_wlan_handle_rxq(struct wilc *wilc)
+{
+ int size;
+ u8 *buffer;
+ struct rxq_entry_t *rqe;
+
+ while (!wilc->quit) {
+ rqe = wilc_wlan_rxq_remove(wilc);
+ if (!rqe)
+ break;
+
+ buffer = rqe->buffer;
+ size = rqe->buffer_size;
+ wilc_wlan_handle_rx_buff(wilc, buffer, size);
+
+ kfree(rqe);
+ }
+ if (wilc->quit)
+ complete(&wilc->cfg_event);
+}
+
+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;
+
+ size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2;
+
+ while (!size && retries < 10) {
+ wilc->hif_func->hif_read_size(wilc, &size);
+ size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, size) << 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)
+ return;
+
+ offset += size;
+ wilc->rx_buffer_offset = offset;
+ rqe = kmalloc_obj(*rqe);
+ if (!rqe)
+ return;
+
+ rqe->buffer = buffer;
+ rqe->buffer_size = size;
+ wilc_wlan_rxq_add(wilc, rqe);
+ wilc_wlan_handle_rxq(wilc);
+}
+
+void wilc_handle_isr(struct wilc *wilc)
+{
+ u32 int_status;
+ int ret;
+
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret) {
+ dev_err_ratelimited(wilc->dev, "Cannot acquire bus\n");
+ return;
+ }
+
+ 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)))
+ wilc_unknown_isr_ext(wilc);
+
+ ret = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+ if (ret)
+ dev_err_ratelimited(wilc->dev, "Cannot release bus\n");
+}
+EXPORT_SYMBOL_GPL(wilc_handle_isr);
+
+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;
+ int rv;
+
+ blksz = BIT(12);
+
+ dma_buffer = kmalloc(blksz, GFP_KERNEL);
+ if (!dma_buffer)
+ return -EIO;
+
+ offset = 0;
+ pr_debug("%s: Downloading firmware size = %d\n", __func__, buffer_size);
+
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ goto fail;
+
+ 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 (reg & BIT(10))
+ pr_err("%s: Failed to reset\n", __func__);
+
+ ret = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
+ if (ret)
+ goto fail;
+
+ do {
+ addr = get_unaligned_le32(&buffer[offset]);
+ size = get_unaligned_le32(&buffer[offset + 4]);
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ goto fail;
+
+ offset += 8;
+ while (((int)size) && (offset < buffer_size)) {
+ size2 = min(size, 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;
+ }
+ rv = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+ if (!ret && rv)
+ ret = rv;
+
+ if (ret) {
+ pr_err("%s Bus error\n", __func__);
+ goto fail;
+ }
+ pr_debug("%s Offset = %d\n", __func__, offset);
+ } while (offset < buffer_size);
+
+fail:
+
+ kfree(dma_buffer);
+
+ return ret;
+}
+
+int wilc_wlan_start(struct wilc *wilc)
+{
+ u32 reg = 0;
+ int ret, rv;
+ u32 chipid;
+
+ if (wilc->io_type == WILC_HIF_SDIO) {
+ reg = 0;
+ reg |= BIT(3);
+ } else if (wilc->io_type == WILC_HIF_SPI) {
+ reg = 1;
+ }
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
+ if (ret)
+ return ret;
+
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg);
+ if (ret)
+ goto release;
+
+ reg = 0;
+ if (wilc->io_type == WILC_HIF_SDIO && wilc->dev_irq_num)
+ reg |= WILC_HAVE_SDIO_IRQ_GPIO;
+
+ if (is_wilc3000(wilc->chipid))
+ reg |= WILC_HAVE_SLEEP_CLK_SRC_RTC;
+
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg);
+ if (ret)
+ goto release;
+
+ wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT);
+
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid);
+ if (ret)
+ goto release;
+
+ 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);
+
+release:
+ rv = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
+ return ret ? ret : rv;
+}
+
+int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif)
+{
+ u32 reg = 0;
+ int ret, rv;
+
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ return ret;
+
+ ret = wilc->hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL, &reg);
+ if (ret)
+ goto release;
+
+ reg &= ~WILC_GLOBAL_MODE_ENABLE_WIFI;
+ ret = wilc->hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL, reg);
+ if (ret)
+ goto release;
+
+ ret = wilc->hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, &reg);
+ if (ret)
+ goto release;
+
+ reg &= ~WILC_PWR_SEQ_ENABLE_WIFI_SLEEP;
+ ret = wilc->hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, reg);
+ if (ret)
+ goto release;
+
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, &reg);
+ if (ret) {
+ netdev_err(vif->ndev, "Error while reading reg\n");
+ goto release;
+ }
+
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0,
+ (reg | WILC_ABORT_REQ_BIT));
+ if (ret) {
+ netdev_err(vif->ndev, "Error while writing reg\n");
+ goto release;
+ }
+
+ ret = 0;
+release:
+ /* host comm is disabled - we can't issue sleep command anymore: */
+ rv = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
+
+ return ret ? ret : rv;
+}
+
+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++) {
+ while ((tqe = wilc_wlan_txq_remove_from_head(wilc, ac))) {
+ if (tqe->tx_complete_func)
+ tqe->tx_complete_func(tqe->priv, 0);
+ kfree(tqe);
+ }
+ }
+
+ while ((rqe = wilc_wlan_rxq_remove(wilc)))
+ kfree(rqe);
+
+ kfree(wilc->vmm_table);
+ wilc->vmm_table = NULL;
+ kfree(wilc->rx_buffer);
+ wilc->rx_buffer = NULL;
+ kfree(wilc->tx_buffer);
+ wilc->tx_buffer = NULL;
+ wilc->hif_func->hif_deinit(wilc);
+}
+
+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 wilc_wlan_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 = wilc_wlan_cfg_set_wid(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;
+ }
+
+ netdev_dbg(vif->ndev, "%s: seqno[%d]\n", __func__, 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)) {
+ netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__);
+ ret_size = 0;
+ }
+
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
+ mutex_unlock(&wilc->cfg_cmd_lock);
+
+ return ret_size;
+}
+
+int wilc_wlan_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 = wilc_wlan_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)) {
+ netdev_dbg(vif->ndev, "%s: Timed Out\n", __func__);
+ ret_size = 0;
+ }
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
+ mutex_unlock(&wilc->cfg_cmd_lock);
+
+ return ret_size;
+}
+
+int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
+ u32 count)
+{
+ int i;
+ int ret = 0;
+ u32 drv = wilc_get_vif_idx(vif);
+
+ if (mode == WILC_GET_CFG) {
+ for (i = 0; i < count; i++) {
+ if (!wilc_wlan_cfg_get(vif, !i,
+ wids[i].id,
+ (i == count - 1),
+ drv)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+ for (i = 0; i < count; i++) {
+ wids[i].size = wilc_wlan_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++) {
+ if (!wilc_wlan_cfg_set(vif, !i,
+ wids[i].id,
+ wids[i].val,
+ wids[i].size,
+ (i == count - 1),
+ drv)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int wilc_get_chipid(struct wilc *wilc)
+{
+ u32 chipid = 0;
+ u32 rfrevid = 0;
+
+ if (wilc->chipid == 0) {
+ wilc->hif_func->hif_read_reg(wilc, WILC3000_CHIP_ID, &chipid);
+ if (!is_wilc3000(chipid)) {
+ wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid);
+ wilc->hif_func->hif_read_reg(wilc, WILC_RF_REVISION_ID,
+ &rfrevid);
+
+ if (!is_wilc1000(chipid)) {
+ wilc->chipid = 0;
+ return -EINVAL;
+ }
+ if (chipid == WILC_1000_BASE_ID_2A) { /* 0x1002A0 */
+ if (rfrevid != 0x1)
+ chipid = WILC_1000_BASE_ID_2A_REV1;
+ } else if (chipid == WILC_1000_BASE_ID_2B) { /* 0x1002B0 */
+ if (rfrevid == 0x4)
+ chipid = WILC_1000_BASE_ID_2B_REV1;
+ else if (rfrevid != 0x3)
+ chipid = WILC_1000_BASE_ID_2B_REV2;
+ }
+ }
+
+ wilc->chipid = chipid;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wilc_get_chipid);
+
+static int init_chip(struct net_device *dev)
+{
+ u32 reg;
+ int ret, rv;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc = vif->wilc;
+
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ return ret;
+
+ ret = wilc_get_chipid(wilc);
+ if (ret)
+ goto release;
+
+ if ((wilc->chipid & 0xfff) != 0xa0) {
+ ret = wilc->hif_func->hif_read_reg(wilc,
+ WILC_CORTUS_RESET_MUX_SEL,
+ &reg);
+ if (ret) {
+ netdev_err(dev, "fail read reg 0x1118\n");
+ goto release;
+ }
+ reg |= BIT(0);
+ ret = wilc->hif_func->hif_write_reg(wilc,
+ WILC_CORTUS_RESET_MUX_SEL,
+ reg);
+ if (ret) {
+ netdev_err(dev, "fail write reg 0x1118\n");
+ goto release;
+ }
+ ret = wilc->hif_func->hif_write_reg(wilc,
+ WILC_CORTUS_BOOT_REGISTER,
+ WILC_CORTUS_BOOT_FROM_IRAM);
+ if (ret) {
+ netdev_err(dev, "fail write reg 0xc0000\n");
+ goto release;
+ }
+ }
+
+ if (is_wilc3000(wilc->chipid)) {
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC3000_BOOTROM_STATUS, &reg);
+ if (ret) {
+ netdev_err(dev, "failed to read WILC3000 BootROM status register\n");
+ goto release;
+ }
+
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC3000_CORTUS_BOOT_REGISTER_2,
+ WILC_CORTUS_BOOT_FROM_IRAM);
+ if (ret) {
+ netdev_err(dev, "failed to write WILC3000 Boot register\n");
+ goto release;
+ }
+ }
+
+release:
+ rv = release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP);
+
+ return ret ? ret : rv;
+}
+
+int wilc_load_mac_from_nv(struct wilc *wl)
+{
+ int ret, rv;
+ unsigned int i;
+
+ ret = acquire_bus(wl, WILC_BUS_ACQUIRE_AND_WAKEUP);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < WILC_NVMEM_MAX_NUM_BANK; i++) {
+ int bank_offset = get_bank_offset_from_bank_index(i);
+ u32 reg1, reg2;
+ u8 invalid;
+ u8 used;
+
+ ret = wl->hif_func->hif_read_reg(wl,
+ WILC_NVMEM_BANK_BASE + bank_offset,
+ &reg1);
+ if (ret) {
+ pr_err("Can not read address %d lower part", i);
+ break;
+ }
+ ret = wl->hif_func->hif_read_reg(wl,
+ WILC_NVMEM_BANK_BASE + bank_offset + 4,
+ &reg2);
+ if (ret) {
+ pr_err("Can not read address %d upper part", i);
+ break;
+ }
+
+ used = FIELD_GET(WILC_NVMEM_IS_BANK_USED, reg1);
+ invalid = FIELD_GET(WILC_NVMEM_IS_BANK_INVALID, reg1);
+ if (!used || invalid)
+ continue;
+
+ wl->nv_mac_address[0] = FIELD_GET(GENMASK(23, 16), reg1);
+ wl->nv_mac_address[1] = FIELD_GET(GENMASK(15, 8), reg1);
+ wl->nv_mac_address[2] = FIELD_GET(GENMASK(7, 0), reg1);
+ wl->nv_mac_address[3] = FIELD_GET(GENMASK(31, 24), reg2);
+ wl->nv_mac_address[4] = FIELD_GET(GENMASK(23, 16), reg2);
+ wl->nv_mac_address[5] = FIELD_GET(GENMASK(15, 8), reg2);
+
+ ret = 0;
+ break;
+ }
+
+ rv = release_bus(wl, WILC_BUS_RELEASE_ALLOW_SLEEP);
+ return ret ? ret : rv;
+}
+EXPORT_SYMBOL_GPL(wilc_load_mac_from_nv);
+
+int wilc_wlan_init(struct net_device *dev)
+{
+ int ret = 0, rv;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+
+ wilc = vif->wilc;
+
+ wilc->quit = 0;
+
+ if (!wilc->hif_func->hif_is_init(wilc)) {
+ ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY);
+ if (ret)
+ return ret;
+
+ ret = wilc->hif_func->hif_init(wilc, false);
+ if (!ret)
+ ret = wilc_get_chipid(wilc);
+ rv = release_bus(wilc, WILC_BUS_RELEASE_ONLY);
+ if (!ret && rv)
+ ret = rv;
+ if (ret)
+ goto fail;
+
+ if (!is_wilc1000(wilc->chipid) && !is_wilc3000(wilc->chipid)) {
+ netdev_err(dev, "Unsupported chipid: %x\n", wilc->chipid);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ netdev_dbg(dev, "chipid (%08x)\n", wilc->chipid);
+ }
+
+ if (!wilc->vmm_table)
+ wilc->vmm_table = kcalloc(WILC_VMM_TBL_SIZE, sizeof(u32), GFP_KERNEL);
+
+ if (!wilc->vmm_table) {
+ ret = -ENOBUFS;
+ goto fail;
+ }
+
+ if (!wilc->tx_buffer)
+ wilc->tx_buffer = kmalloc(WILC_TX_BUFF_SIZE, GFP_KERNEL);
+
+ if (!wilc->tx_buffer) {
+ ret = -ENOBUFS;
+ goto fail;
+ }
+
+ if (!wilc->rx_buffer)
+ wilc->rx_buffer = kmalloc(WILC_RX_BUFF_SIZE, GFP_KERNEL);
+
+ if (!wilc->rx_buffer) {
+ ret = -ENOBUFS;
+ goto fail;
+ }
+
+ if (init_chip(dev)) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ kfree(wilc->vmm_table);
+ wilc->vmm_table = NULL;
+ kfree(wilc->rx_buffer);
+ wilc->rx_buffer = NULL;
+ kfree(wilc->tx_buffer);
+ wilc->tx_buffer = NULL;
+
+ return ret;
+}
diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.h b/drivers/net/wireless/microchip/wilc1000/wlan.h
new file mode 100644
index 000000000..b9e7f9222
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/wlan.h
@@ -0,0 +1,475 @@
+/* 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/bitfield.h>
+
+/********************************************
+ *
+ * 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)
+
+/********************************************
+ *
+ * Register Defines
+ *
+ ********************************************/
+#define WILC_PERIPH_REG_BASE 0x1000
+#define WILC_CHANGING_VIR_IF 0x108c
+#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_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_RF_REVISION_ID 0x13f4
+
+#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_INT_STATUS (WILC_SPI_REG_BASE + 0x40)
+#define WILC_SPI_INT_CLEAR (WILC_SPI_REG_BASE + 0x44)
+
+#define WILC_SPI_WAKEUP_REG 0x1
+#define WILC_SPI_WAKEUP_BIT BIT(1)
+
+/* WILC1000 specific */
+#define WILC1000_SPI_CLK_STATUS_REG 0x0f
+#define WILC1000_SPI_CLK_STATUS_BIT BIT(2)
+
+/* WILC3000 specific */
+#define WILC3000_SPI_CLK_STATUS_REG 0x13
+#define WILC3000_SPI_CLK_STATUS_BIT BIT(2)
+
+#define WILC_SPI_HOST_TO_FW_REG 0x0b
+#define WILC_SPI_HOST_TO_FW_BIT BIT(0)
+
+#define WILC_SPI_FW_TO_HOST_REG 0x10
+#define WILC_SPI_FW_TO_HOST_BIT BIT(0)
+
+#define WILC_SPI_PROTOCOL_OFFSET (WILC_SPI_PROTOCOL_CONFIG - \
+ WILC_SPI_REG_BASE)
+
+#define WILC_SPI_CLOCKLESS_ADDR_LIMIT 0x30
+
+/* Functions IO enables bits */
+#define WILC_SDIO_CCCR_IO_EN_FUNC1 BIT(1)
+
+/* Function/Interrupt enables bits */
+#define WILC_SDIO_CCCR_IEN_MASTER BIT(0)
+#define WILC_SDIO_CCCR_IEN_FUNC1 BIT(1)
+
+/* Abort CCCR register bits */
+#define WILC_SDIO_CCCR_ABORT_RESET BIT(3)
+
+/* Vendor specific CCCR registers */
+#define WILC_SDIO_WAKEUP_REG 0xf0
+#define WILC_SDIO_WAKEUP_BIT BIT(0)
+
+/* WILC1000 */
+#define WILC1000_SDIO_CLK_STATUS_REG 0xf1
+#define WILC1000_SDIO_CLK_STATUS_BIT BIT(0)
+
+#define WILC1000_SDIO_IRQ_FLAG_REG 0xf7
+#define WILC1000_SDIO_IRQ_CLEAR_FLAG_REG 0xf8
+
+/* WILC3000 specific */
+#define WILC3000_SDIO_CLK_STATUS_REG 0xf0 /* clk & wakeup are on same reg */
+#define WILC3000_SDIO_CLK_STATUS_BIT BIT(4)
+
+#define WILC3000_SDIO_VMM_TBL_CTRL_REG 0xf1
+#define WILC3000_SDIO_IRQ_FLAG_REG 0xfe
+
+/* Common vendor specific CCCR register */
+#define WILC_SDIO_INTERRUPT_DATA_SZ_REG 0xf2 /* Read size (2 bytes) */
+
+#define WILC_SDIO_VMM_TBL_CTRL_REG 0xf6
+
+#define WILC_SDIO_HOST_TO_FW_REG 0xfa
+#define WILC_SDIO_HOST_TO_FW_BIT BIT(0)
+
+#define WILC_SDIO_FW_TO_HOST_REG 0xfc
+#define WILC_SDIO_FW_TO_HOST_BIT BIT(0)
+
+/* Function 1 specific FBR register */
+#define WILC_SDIO_FBR_CSA_REG 0x10C /* CSA pointer (3 bytes) */
+#define WILC_SDIO_FBR_DATA_REG 0x10F
+
+#define WILC_SDIO_F1_DATA_REG 0x0
+#define WILC_SDIO_EXT_IRQ_FLAG_REG 0x4
+
+#define WILC_AHB_DATA_MEM_BASE 0x30000
+#define WILC_AHB_SHARE_MEM_BASE 0xd0000
+
+#define WILC_VMM_TBL_RX_SHADOW_BASE WILC_AHB_SHARE_MEM_BASE
+#define WILC_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 GLOBAL_MODE_CONTROL 0x1614
+#define PWR_SEQ_MISC_CTRL 0x3008
+
+#define WILC_GLOBAL_MODE_ENABLE_WIFI BIT(0)
+#define WILC_PWR_SEQ_ENABLE_WIFI_SLEEP BIT(28)
+
+#define WILC_HAVE_SDIO_IRQ_GPIO BIT(0)
+#define WILC_HAVE_USE_PMU BIT(1)
+#define WILC_HAVE_SLEEP_CLK_SRC_RTC BIT(2)
+#define WILC_HAVE_SLEEP_CLK_SRC_XO BIT(3)
+#define WILC_HAVE_EXT_PA_INV_TX_RX BIT(4)
+#define WILC_HAVE_LEGACY_RF_SETTINGS BIT(5)
+#define WILC_HAVE_XTAL_24 BIT(6)
+#define WILC_HAVE_DISABLE_WILC_UART BIT(7)
+#define WILC_HAVE_USE_IRQ_AS_HOST_WAKE BIT(8)
+
+#define WILC_CORTUS_INTERRUPT_BASE 0x10A8
+#define WILC1000_CORTUS_INTERRUPT_1 (WILC_CORTUS_INTERRUPT_BASE + 0x4)
+#define WILC3000_CORTUS_INTERRUPT_1 (WILC_CORTUS_INTERRUPT_BASE + 0x14)
+
+#define WILC1000_CORTUS_INTERRUPT_2 (WILC_CORTUS_INTERRUPT_BASE + 0x8)
+#define WILC3000_CORTUS_INTERRUPT_2 (WILC_CORTUS_INTERRUPT_BASE + 0x18)
+
+/* tx control register 1 to 4 for RX */
+#define WILC_REG_4_TO_1_RX 0x1e1c
+
+/* tx control register 1 to 4 for TX Bank_0 */
+#define WILC_REG_4_TO_1_TX_BANK0 0x1e9c
+
+#define WILC_CORTUS_RESET_MUX_SEL 0x1118
+#define WILC_CORTUS_BOOT_REGISTER 0xc0000
+#define WILC3000_BOOTROM_STATUS 0x207ac
+#define WILC3000_CORTUS_BOOT_REGISTER_2 0x4f0000
+#define WILC3000_CHIP_ID 0x3b0000
+
+#define WILC_CORTUS_BOOT_FROM_IRAM 0x71
+
+#define WILC_1000_BASE_ID 0x100000
+
+#define WILC_1000_BASE_ID_2A 0x1002A0
+#define WILC_1000_BASE_ID_2A_REV1 (WILC_1000_BASE_ID_2A + 1)
+
+#define WILC_1000_BASE_ID_2B 0x1002B0
+#define WILC_1000_BASE_ID_2B_REV1 (WILC_1000_BASE_ID_2B + 1)
+#define WILC_1000_BASE_ID_2B_REV2 (WILC_1000_BASE_ID_2B + 2)
+
+#define WILC_3000_BASE_ID 0x300000
+
+#define WILC_CHIP_REV_FIELD GENMASK(11, 0)
+
+/********************************************
+ *
+ * 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 WILC_ABORT_REQ_BIT BIT(31)
+
+#define WILC_RX_BUFF_SIZE (96 * 1024)
+#define WILC_TX_BUFF_SIZE (64 * 1024)
+
+#define NQUEUES 4
+#define AC_BUFFER_SIZE 1000
+
+#define VO_AC_COUNT_FIELD GENMASK(31, 25)
+#define VO_AC_ACM_STAT_FIELD BIT(24)
+#define VI_AC_COUNT_FIELD GENMASK(23, 17)
+#define VI_AC_ACM_STAT_FIELD BIT(16)
+#define BE_AC_COUNT_FIELD GENMASK(15, 9)
+#define BE_AC_ACM_STAT_FIELD BIT(8)
+#define BK_AC_COUNT_FIELD GENMASK(7, 3)
+#define BK_AC_ACM_STAT_FIELD BIT(1)
+
+#define WILC_PKT_HDR_CONFIG_FIELD BIT(31)
+#define WILC_PKT_HDR_OFFSET_FIELD GENMASK(30, 22)
+#define WILC_PKT_HDR_TOTAL_LEN_FIELD GENMASK(21, 11)
+#define WILC_PKT_HDR_LEN_FIELD GENMASK(10, 0)
+
+#define WILC_INTERRUPT_DATA_SIZE GENMASK(14, 0)
+
+#define WILC_VMM_BUFFER_SIZE GENMASK(9, 0)
+
+#define WILC_VMM_HDR_TYPE BIT(31)
+#define WILC_VMM_HDR_MGMT_FIELD BIT(30)
+#define WILC_VMM_HDR_PKT_SIZE GENMASK(29, 15)
+#define WILC_VMM_HDR_BUFF_SIZE GENMASK(14, 0)
+
+#define WILC_VMM_ENTRY_COUNT GENMASK(8, 3)
+#define WILC_VMM_ENTRY_AVAILABLE BIT(2)
+/*******************************************/
+/* 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 GENMASK(IRG_FLAGS_OFFSET - 1, 0)
+#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 INT_5 BIT(IRG_FLAGS_OFFSET + 5)
+#define MAX_NUM_INT 5
+#define IRG_FLAGS_MASK GENMASK(IRG_FLAGS_OFFSET + MAX_NUM_INT, \
+ IRG_FLAGS_OFFSET)
+
+/*******************************************/
+/* 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 UNHANDLED_IRQ_MASK GENMASK(MAX_NUM_INT - 1, NUM_INT_EXT)
+
+#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_MGMT_AUTH_PKT 0x010
+
+#define WILC_WID_TYPE GENMASK(15, 12)
+#define WILC_VMM_ENTRY_FULL_RETRY 1
+/********************************************
+ *
+ * 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;
+ struct wilc_vif *vif;
+ void (*tx_complete_func)(void *priv, int status);
+};
+
+struct txq_fw_recv_queue_stat {
+ u8 acm;
+ u8 count;
+};
+
+struct txq_handle {
+ struct txq_entry_t txq_head;
+ u16 count;
+ struct txq_fw_recv_queue_stat fw;
+};
+
+struct rxq_entry_t {
+ struct list_head list;
+ u8 *buffer;
+ int buffer_size;
+};
+
+/********************************************
+ *
+ * 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;
+ struct sk_buff *skb;
+};
+
+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_vif;
+
+static inline bool is_wilc1000(u32 id)
+{
+ return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID;
+}
+
+static inline bool is_wilc3000(u32 id)
+{
+ return (id & (~WILC_CHIP_REV_FIELD)) == WILC_3000_BASE_ID;
+}
+
+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 wilc_wlan_txq_add_net_pkt(struct net_device *dev,
+ struct tx_complete_data *tx_data, u8 *buffer,
+ u32 buffer_size,
+ void (*tx_complete_fn)(void *, int));
+int wilc_wlan_handle_txq(struct wilc *wl, u32 *txq_count);
+void wilc_handle_isr(struct wilc *wilc);
+void wilc_wlan_cleanup(struct net_device *dev);
+int wilc_wlan_cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer,
+ u32 buffer_size, int commit, u32 drv_handler);
+int wilc_wlan_cfg_get(struct wilc_vif *vif, int start, u16 wid, int commit,
+ u32 drv_handler);
+int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size, void (*func)(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 wilc_vif *vif, u8 *buff, u32 size);
+bool wilc_wfi_mgmt_frame_rx(struct wilc_vif *vif, u8 *buff, u32 size);
+int host_wakeup_notify(struct wilc *wilc);
+int host_sleep_notify(struct wilc *wilc);
+int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids,
+ u32 count);
+int wilc_wlan_init(struct net_device *dev);
+int wilc_get_chipid(struct wilc *wilc);
+int wilc_load_mac_from_nv(struct wilc *wilc);
+#endif
diff --git a/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c b/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c
new file mode 100644
index 000000000..7664403c8
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
+ * All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include "wlan_if.h"
+#include "wlan.h"
+#include "wlan_cfg.h"
+#include "netdev.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 const 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 const struct wilc_cfg_hword g_cfg_hword[] = {
+ {WID_NIL, 0}
+};
+
+static const 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 const struct wilc_cfg_str g_cfg_str[] = {
+ {WID_FIRMWARE_VERSION, 0, NULL},
+ {WID_MAC_ADDR, 0, NULL},
+ {WID_ASSOC_RES_INFO, 0, NULL},
+ {WID_NIL, 0, NULL}
+};
+
+#define WILC_RESP_MSG_TYPE_CONFIG_REPLY 'R'
+#define WILC_RESP_MSG_TYPE_STATUS_INFO 'I'
+#define WILC_RESP_MSG_TYPE_NETWORK_INFO 'N'
+#define WILC_RESP_MSG_TYPE_SCAN_COMPLETE 'S'
+
+/********************************************
+ *
+ * Configuration Functions
+ *
+ ********************************************/
+
+static int wilc_wlan_cfg_set_byte(u8 *frame, u32 offset, u16 id, u8 val8)
+{
+ if ((offset + 4) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ put_unaligned_le16(id, &frame[offset]);
+ put_unaligned_le16(1, &frame[offset + 2]);
+ frame[offset + 4] = val8;
+ return 5;
+}
+
+static int wilc_wlan_cfg_set_hword(u8 *frame, u32 offset, u16 id, u16 val16)
+{
+ if ((offset + 5) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ put_unaligned_le16(id, &frame[offset]);
+ put_unaligned_le16(2, &frame[offset + 2]);
+ put_unaligned_le16(val16, &frame[offset + 4]);
+
+ return 6;
+}
+
+static int wilc_wlan_cfg_set_word(u8 *frame, u32 offset, u16 id, u32 val32)
+{
+ if ((offset + 7) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ put_unaligned_le16(id, &frame[offset]);
+ put_unaligned_le16(4, &frame[offset + 2]);
+ put_unaligned_le32(val32, &frame[offset + 4]);
+
+ return 8;
+}
+
+static int wilc_wlan_cfg_set_str(u8 *frame, u32 offset, u16 id, u8 *str,
+ u32 size)
+{
+ if ((offset + size + 4) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ put_unaligned_le16(id, &frame[offset]);
+ put_unaligned_le16(size, &frame[offset + 2]);
+ if (str && size != 0)
+ memcpy(&frame[offset + 4], str, size);
+
+ return (size + 4);
+}
+
+static int wilc_wlan_cfg_set_bin(u8 *frame, u32 offset, u16 id, u8 *b, u32 size)
+{
+ u32 i;
+ u8 checksum = 0;
+
+ if ((offset + size + 5) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ put_unaligned_le16(id, &frame[offset]);
+ put_unaligned_le16(size, &frame[offset + 2]);
+
+ if ((b) && size != 0) {
+ memcpy(&frame[offset + 4], b, size);
+ for (i = 0; i < size; i++)
+ checksum += frame[offset + i + 4];
+ }
+
+ frame[offset + size + 4] = checksum;
+
+ return (size + 5);
+}
+
+/********************************************
+ *
+ * Configuration Response Functions
+ *
+ ********************************************/
+
+static void wilc_wlan_parse_response_frame(struct wilc *wl, u8 *info, int size)
+{
+ u16 wid;
+ u32 len = 0, i = 0;
+ struct wilc_cfg *cfg = &wl->cfg;
+
+ while (size > 0) {
+ i = 0;
+ wid = get_unaligned_le16(info);
+
+ switch (FIELD_GET(WILC_WID_TYPE, wid)) {
+ case WID_CHAR:
+ len = 3;
+ if (len + 2 > size)
+ return;
+
+ while (cfg->b[i].id != WID_NIL && cfg->b[i].id != wid)
+ i++;
+
+ if (cfg->b[i].id == wid)
+ cfg->b[i].val = info[4];
+
+ break;
+
+ case WID_SHORT:
+ len = 4;
+ if (len + 2 > size)
+ return;
+
+ while (cfg->hw[i].id != WID_NIL && cfg->hw[i].id != wid)
+ i++;
+
+ if (cfg->hw[i].id == wid)
+ cfg->hw[i].val = get_unaligned_le16(&info[4]);
+
+ break;
+
+ case WID_INT:
+ len = 6;
+ if (len + 2 > size)
+ return;
+
+ while (cfg->w[i].id != WID_NIL && cfg->w[i].id != wid)
+ i++;
+
+ if (cfg->w[i].id == wid)
+ cfg->w[i].val = get_unaligned_le32(&info[4]);
+
+ break;
+
+ case WID_STR:
+ len = 2 + get_unaligned_le16(&info[2]);
+
+ while (cfg->s[i].id != WID_NIL && cfg->s[i].id != wid)
+ i++;
+
+ if (cfg->s[i].id == wid) {
+ if (len > cfg->s[i].len || (len + 2 > size))
+ return;
+
+ memcpy(cfg->s[i].str, &info[2],
+ len);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ size -= (2 + len);
+ info += (2 + len);
+ }
+}
+
+static void wilc_wlan_parse_info_frame(struct wilc *wl, u8 *info)
+{
+ u32 wid, len;
+
+ wid = get_unaligned_le16(info);
+
+ len = info[2];
+
+ if (len == 1 && wid == WID_STATUS) {
+ int i = 0;
+
+ while (wl->cfg.b[i].id != WID_NIL &&
+ wl->cfg.b[i].id != wid)
+ i++;
+
+ if (wl->cfg.b[i].id == wid)
+ wl->cfg.b[i].val = info[3];
+ }
+}
+
+/********************************************
+ *
+ * Configuration Exported Functions
+ *
+ ********************************************/
+
+int wilc_wlan_cfg_set_wid(u8 *frame, u32 offset, u16 id, u8 *buf, int size)
+{
+ u8 type = FIELD_GET(WILC_WID_TYPE, id);
+ 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;
+ }
+
+ return ret;
+}
+
+int wilc_wlan_cfg_get_wid(u8 *frame, u32 offset, u16 id)
+{
+ if ((offset + 2) >= WILC_MAX_CFG_FRAME_SIZE)
+ return 0;
+
+ put_unaligned_le16(id, &frame[offset]);
+
+ return 2;
+}
+
+int wilc_wlan_cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer,
+ u32 buffer_size)
+{
+ u8 type = FIELD_GET(WILC_WID_TYPE, wid);
+ int i, ret = 0;
+ struct wilc_cfg *cfg = &wl->cfg;
+
+ i = 0;
+ if (type == CFG_BYTE_CMD) {
+ while (cfg->b[i].id != WID_NIL && cfg->b[i].id != wid)
+ i++;
+
+ if (cfg->b[i].id == wid) {
+ memcpy(buffer, &cfg->b[i].val, 1);
+ ret = 1;
+ }
+ } else if (type == CFG_HWORD_CMD) {
+ while (cfg->hw[i].id != WID_NIL && cfg->hw[i].id != wid)
+ i++;
+
+ if (cfg->hw[i].id == wid) {
+ memcpy(buffer, &cfg->hw[i].val, 2);
+ ret = 2;
+ }
+ } else if (type == CFG_WORD_CMD) {
+ while (cfg->w[i].id != WID_NIL && cfg->w[i].id != wid)
+ i++;
+
+ if (cfg->w[i].id == wid) {
+ memcpy(buffer, &cfg->w[i].val, 4);
+ ret = 4;
+ }
+ } else if (type == CFG_STR_CMD) {
+ while (cfg->s[i].id != WID_NIL && cfg->s[i].id != wid)
+ i++;
+
+ if (cfg->s[i].id == wid) {
+ u16 size = get_unaligned_le16(cfg->s[i].str);
+
+ if (buffer_size >= size) {
+ memcpy(buffer, &cfg->s[i].str[2], size);
+ ret = size;
+ }
+ }
+ }
+ return ret;
+}
+
+void wilc_wlan_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;
+
+ switch (msg_type) {
+ case WILC_RESP_MSG_TYPE_CONFIG_REPLY:
+ wilc_wlan_parse_response_frame(wilc, frame, size);
+ rsp->type = WILC_CFG_RSP;
+ rsp->seq_no = msg_id;
+ break;
+
+ case WILC_RESP_MSG_TYPE_STATUS_INFO:
+ 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 */
+ wilc_gnrl_async_info_received(wilc, frame - 4, size + 4);
+ break;
+
+ case WILC_RESP_MSG_TYPE_NETWORK_INFO:
+ wilc_network_info_received(wilc, frame - 4, size + 4);
+ break;
+
+ case WILC_RESP_MSG_TYPE_SCAN_COMPLETE:
+ wilc_scan_complete_received(wilc, frame - 4, size + 4);
+ break;
+
+ default:
+ rsp->seq_no = msg_id;
+ break;
+ }
+}
+
+int wilc_wlan_cfg_init(struct wilc *wl)
+{
+ struct wilc_cfg_str_vals *str_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_obj(*str_vals);
+ if (!str_vals)
+ goto out_s;
+
+ wl->cfg.str_vals = str_vals;
+ /* store the string cfg parameters */
+ wl->cfg.s[i].id = WID_FIRMWARE_VERSION;
+ wl->cfg.s[i].str = str_vals->firmware_version;
+ wl->cfg.s[i].len = sizeof(str_vals->firmware_version);
+ i++;
+ wl->cfg.s[i].id = WID_MAC_ADDR;
+ wl->cfg.s[i].str = str_vals->mac_address;
+ wl->cfg.s[i].len = sizeof(str_vals->mac_address);
+ i++;
+ wl->cfg.s[i].id = WID_ASSOC_RES_INFO;
+ wl->cfg.s[i].str = str_vals->assoc_rsp;
+ wl->cfg.s[i].len = sizeof(str_vals->assoc_rsp);
+ i++;
+ wl->cfg.s[i].id = WID_NIL;
+ wl->cfg.s[i].str = NULL;
+ return 0;
+
+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 wilc_wlan_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);
+}
diff --git a/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h b/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h
new file mode 100644
index 000000000..5ae74bced
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h
@@ -0,0 +1,55 @@
+/* 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 {
+ u16 id;
+ u32 val;
+};
+
+struct wilc_cfg_str {
+ u16 id;
+ u16 len;
+ u8 *str;
+};
+
+struct wilc_cfg_str_vals {
+ u8 mac_address[8];
+ u8 firmware_version[130];
+ u8 assoc_rsp[WILC_MAX_ASSOC_RESP_FRAME_SIZE];
+};
+
+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;
+int wilc_wlan_cfg_set_wid(u8 *frame, u32 offset, u16 id, u8 *buf, int size);
+int wilc_wlan_cfg_get_wid(u8 *frame, u32 offset, u16 id);
+int wilc_wlan_cfg_get_val(struct wilc *wl, u16 wid, u8 *buffer,
+ u32 buffer_size);
+void wilc_wlan_cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+ struct wilc_cfg_rsp *rsp);
+int wilc_wlan_cfg_init(struct wilc *wl);
+void wilc_wlan_cfg_deinit(struct wilc *wl);
+
+#endif
diff --git a/drivers/net/wireless/microchip/wilc1000/wlan_if.h b/drivers/net/wireless/microchip/wilc1000/wlan_if.h
new file mode 100644
index 000000000..254a046e3
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/wlan_if.h
@@ -0,0 +1,814 @@
+/* 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 "fw.h"
+
+#define WILC_MAX_ASSOC_RESP_FRAME_SIZE 512
+
+/********************************************
+ *
+ * Wlan Configuration ID
+ *
+ ********************************************/
+
+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 */
+};
+
+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,
+ WILC_FW_AUTH_SAE = 7,
+ WILC_FW_AUTH_IEE8021X_SHA256 = 9,
+ WILC_FW_AUTH_OPEN_SYSTEM_SHA256 = 13
+};
+
+enum mfptype {
+ WILC_FW_MFP_NONE = 0x0,
+ WILC_FW_MFP_OPTIONAL = 0x1,
+ WILC_FW_MFP_REQUIRED = 0x2
+};
+
+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,
+ WILC_FW_AUTH_REQ_IDX = 2
+};
+
+enum wid_type {
+ WID_CHAR = 0,
+ WID_SHORT = 1,
+ WID_INT = 2,
+ WID_STR = 3,
+ WID_BIN_DATA = 4,
+ WID_BIN = 5,
+};
+
+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,
+ WID_SET_MFP = 0x00D0,
+
+ WID_DEFAULT_MGMT_KEY_ID = 0x00D2,
+ /* 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,
+ /* 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_ADD_IGTK = 0x3022,
+ 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_OPERATION_MODE = 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_EXTERNAL_AUTH_PARAM = 0x408d,
+ /* Miscellaneous WIDs */
+ WID_ALL = 0x7FFE,
+ WID_MAX = 0xFFFF
+};
+
+#endif