summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorAjay Singh <ajay.kathat@microchip.com>2020-06-25 12:37:23 +0000
committerKalle Valo <kvalo@codeaurora.org>2020-06-26 08:46:46 +0300
commit5625f965d7644b4dc6a71d74021cfe093ad34eea (patch)
tree967b0633a872c1bc9cce5bfb0dcea8ef6a8f4c49 /drivers/net/wireless
parentb3a9e3b9622ae10064826dccb4f7a52bd88c7407 (diff)
wilc1000: move wilc driver out of staging
WILC1000 is an IEEE 802.11 b/g/n IoT link controller module. The WILC1000 connects to Microchip AVR/SMART MCUs, SMART MPUs, and other processors with minimal resource requirements with a simple SPI/SDIO-to-Wi-Fi interface. WILC1000 driver has been part of staging for few years. With contributions from the community, it has improved significantly. Full driver review has helped in achieving the current state. The details for those reviews are captured in 1 & 2. [1]. https://lore.kernel.org/linux-wireless/1537957525-11467-1-git-send-email-ajay.kathat@microchip.com/ [2]. https://lore.kernel.org/linux-wireless/1562896697-8002-1-git-send-email-ajay.kathat@microchip.com/ Signed-off-by: Ajay Singh <ajay.kathat@microchip.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/microchip/Kconfig15
-rw-r--r--drivers/net/wireless/microchip/Makefile2
-rw-r--r--drivers/net/wireless/microchip/wilc1000/Kconfig47
-rw-r--r--drivers/net/wireless/microchip/wilc1000/Makefile14
-rw-r--r--drivers/net/wireless/microchip/wilc1000/cfg80211.c1847
-rw-r--r--drivers/net/wireless/microchip/wilc1000/cfg80211.h30
-rw-r--r--drivers/net/wireless/microchip/wilc1000/fw.h119
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.c1961
-rw-r--r--drivers/net/wireless/microchip/wilc1000/hif.h214
-rw-r--r--drivers/net/wireless/microchip/wilc1000/mon.c260
-rw-r--r--drivers/net/wireless/microchip/wilc1000/netdev.c931
-rw-r--r--drivers/net/wireless/microchip/wilc1000/netdev.h287
-rw-r--r--drivers/net/wireless/microchip/wilc1000/sdio.c1023
-rw-r--r--drivers/net/wireless/microchip/wilc1000/spi.c945
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.c1238
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.h397
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_cfg.c413
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_cfg.h54
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan_if.h803
21 files changed, 10602 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 8ab62bb6b853..75f18c1e5009 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -40,6 +40,7 @@ source "drivers/net/wireless/intel/Kconfig"
source "drivers/net/wireless/intersil/Kconfig"
source "drivers/net/wireless/marvell/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
+source "drivers/net/wireless/microchip/Kconfig"
source "drivers/net/wireless/ralink/Kconfig"
source "drivers/net/wireless/realtek/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 6cfe74515c95..80b324499786 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/
obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
+obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
diff --git a/drivers/net/wireless/microchip/Kconfig b/drivers/net/wireless/microchip/Kconfig
new file mode 100644
index 000000000000..a6b46fb6b1ec
--- /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 000000000000..73b763c7393e
--- /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 000000000000..80c92e8bf8a5
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/Kconfig
@@ -0,0 +1,47 @@
+# 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
+ 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 devision interrupt.
diff --git a/drivers/net/wireless/microchip/wilc1000/Makefile b/drivers/net/wireless/microchip/wilc1000/Makefile
new file mode 100644
index 000000000000..a3305a0a888a
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_WILC1000) += wilc1000.o
+
+ccflags-y += -DFIRMWARE_1002=\"atmel/wilc1002_firmware.bin\" \
+ -DFIRMWARE_1003=\"atmel/wilc1003_firmware.bin\"
+
+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 000000000000..b6065a0d660f
--- /dev/null
+++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
@@ -0,0 +1,1847 @@
+// 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 = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4)
+ }
+};
+
+static const struct wiphy_wowlan_support wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY
+};
+
+struct wilc_p2p_mgmt_data {
+ int size;
+ u8 *buff;
+};
+
+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, void *user_void)
+{
+ struct wilc_priv *priv = user_void;
+
+ if (!priv->cfg_scanning)
+ return;
+
+ if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
+ s32 freq;
+ struct ieee80211_channel *channel;
+ struct cfg80211_bss *bss;
+ struct wiphy *wiphy = priv->dev->ieee80211_ptr->wiphy;
+
+ if (!wiphy || !info)
+ return;
+
+ freq = ieee80211_channel_to_frequency((s32)info->ch,
+ NL80211_BAND_2GHZ);
+ channel = ieee80211_get_channel(wiphy, freq);
+ if (!channel)
+ return;
+
+ bss = cfg80211_inform_bss_frame(wiphy, channel, info->mgmt,
+ info->frame_len,
+ (s32)info->rssi * 100,
+ GFP_KERNEL);
+ if (!bss)
+ cfg80211_put_bss(wiphy, bss);
+ } else if (scan_event == SCAN_EVENT_DONE) {
+ 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,
+ void *priv_data)
+{
+ struct wilc_priv *priv = priv_data;
+ 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 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,
+ request->n_channels, cfg_scan_result, (void *)priv,
+ 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 authtype auth_type = WILC_FW_AUTH_ANY;
+ u32 cipher_group;
+ struct cfg80211_bss *bss;
+ void *join_params;
+ u8 ch;
+
+ vif->connecting = true;
+
+ memset(priv->wep_key, 0, sizeof(priv->wep_key));
+ memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
+
+ cipher_group = sme->crypto.cipher_group;
+ if (cipher_group != 0) {
+ if (cipher_group == WLAN_CIPHER_SUITE_WEP40) {
+ security = WILC_FW_SEC_WEP;
+
+ priv->wep_key_len[sme->key_idx] = sme->key_len;
+ memcpy(priv->wep_key[sme->key_idx], sme->key,
+ sme->key_len);
+
+ wilc_set_wep_default_keyid(vif, sme->key_idx);
+ wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+ sme->key_idx);
+ } else if (cipher_group == WLAN_CIPHER_SUITE_WEP104) {
+ security = WILC_FW_SEC_WEP_EXTENDED;
+
+ priv->wep_key_len[sme->key_idx] = sme->key_len;
+ memcpy(priv->wep_key[sme->key_idx], sme->key,
+ sme->key_len);
+
+ wilc_set_wep_default_keyid(vif, sme->key_idx);
+ wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+ sme->key_idx);
+ } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
+ if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
+ security = WILC_FW_SEC_WPA2_TKIP;
+ else
+ security = WILC_FW_SEC_WPA2_AES;
+ } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
+ if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
+ security = WILC_FW_SEC_WPA_TKIP;
+ else
+ security = WILC_FW_SEC_WPA_AES;
+ } else {
+ ret = -ENOTSUPP;
+ 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_SHARED_KEY:
+ auth_type = WILC_FW_AUTH_SHARED_KEY;
+ 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;
+ }
+
+ 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.ch = ch;
+ wfi_drv->conn_info.conn_result = cfg_connect_result;
+ wfi_drv->conn_info.arg = priv;
+ wfi_drv->conn_info.param = join_params;
+
+ ret = wilc_set_join_req(vif, bss->bssid, sme->ie, sme->ie_len);
+ if (ret) {
+ 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 inline void wilc_wfi_cfg_copy_wep_info(struct wilc_priv *priv,
+ u8 key_index,
+ struct key_params *params)
+{
+ priv->wep_key_len[key_index] = params->key_len;
+ memcpy(priv->wep_key[key_index], params->key, params->key_len);
+}
+
+static int wilc_wfi_cfg_allocate_wpa_entry(struct wilc_priv *priv, u8 idx)
+{
+ if (!priv->wilc_gtk[idx]) {
+ priv->wilc_gtk[idx] = kzalloc(sizeof(*priv->wilc_gtk[idx]),
+ GFP_KERNEL);
+ if (!priv->wilc_gtk[idx])
+ return -ENOMEM;
+ }
+
+ if (!priv->wilc_ptk[idx]) {
+ priv->wilc_ptk[idx] = kzalloc(sizeof(*priv->wilc_ptk[idx]),
+ GFP_KERNEL);
+ if (!priv->wilc_ptk[idx])
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int wilc_wfi_cfg_copy_wpa_info(struct wilc_wfi_key *key_info,
+ struct key_params *params)
+{
+ kfree(key_info->key);
+
+ key_info->key = kmemdup(params->key, params->key_len, GFP_KERNEL);
+ if (!key_info->key)
+ return -ENOMEM;
+
+ kfree(key_info->seq);
+
+ if (params->seq_len > 0) {
+ key_info->seq = kmemdup(params->seq, params->seq_len,
+ GFP_KERNEL);
+ if (!key_info->seq)
+ return -ENOMEM;
+ }
+
+ key_info->cipher = params->cipher;
+ key_info->key_len = params->key_len;
+ key_info->seq_len = params->seq_len;
+
+ return 0;
+}
+
+static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr, struct key_params *params)
+
+{
+ int ret = 0, keylen = params->key_len;
+ 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(netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (priv->wdev.iftype == NL80211_IFTYPE_AP) {
+ wilc_wfi_cfg_copy_wep_info(priv, key_index, params);
+
+ if (params->cipher == WLAN_CIPHER_SUITE_WEP40)
+ mode = WILC_FW_SEC_WEP;
+ else
+ mode = WILC_FW_SEC_WEP_EXTENDED;
+
+ ret = wilc_add_wep_key_bss_ap(vif, params->key,
+ params->key_len,
+ key_index, mode,
+ WILC_FW_AUTH_OPEN_SYSTEM);
+ break;
+ }
+ if (memcmp(params->key, priv->wep_key[key_index],
+ params->key_len)) {
+ wilc_wfi_cfg_copy_wep_info(priv, key_index, params);
+
+ ret = wilc_add_wep_key_bss_sta(vif, params->key,
+ params->key_len,
+ key_index);
+ }
+
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (priv->wdev.iftype == NL80211_IFTYPE_AP ||
+ priv->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ struct wilc_wfi_key *key;
+
+ ret = wilc_wfi_cfg_allocate_wpa_entry(priv, key_index);
+ if (ret)
+ return -ENOMEM;
+
+ if (params->key_len > 16 &&
+ params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ tx_mic = params->key + 24;
+ rx_mic = params->key + 16;
+ keylen = params->key_len - 16;
+ }
+
+ if (!pairwise) {
+ if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+ mode = WILC_FW_SEC_WPA_TKIP;
+ else
+ mode = WILC_FW_SEC_WPA2_AES;
+
+ priv->wilc_groupkey = mode;
+
+ key = priv->wilc_gtk[key_index];
+ } else {
+ if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+ mode = WILC_FW_SEC_WPA_TKIP;
+ else
+ mode = priv->wilc_groupkey | WILC_FW_AES;
+
+ key = priv->wilc_ptk[key_index];
+ }
+ ret = wilc_wfi_cfg_copy_wpa_info(key, params);
+ if (ret)
+ return -ENOMEM;
+
+ op_mode = WILC_AP_MODE;
+ } else {
+ if (params->key_len > 16 &&
+ params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ rx_mic = params->key + 24;
+ tx_mic = params->key + 16;
+ keylen = params->key_len - 16;
+ }
+
+ op_mode = WILC_STATION_MODE;
+ }
+
+ if (!pairwise)
+ ret = wilc_add_rx_gtk(vif, params->key, keylen,
+ key_index, params->seq_len,
+ params->seq, rx_mic, tx_mic,
+ op_mode, mode);
+ else
+ ret = wilc_add_ptk(vif, params->key, keylen, mac_addr,
+ rx_mic, tx_mic, op_mode, mode,
+ key_index);
+
+ break;
+
+ default:
+ netdev_err(netdev, "%s: Unsupported cipher\n", __func__);
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static int del_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index,
+ bool pairwise,
+ const u8 *mac_addr)
+{
+ struct wilc_vif *vif = netdev_priv(netdev);
+ struct wilc_priv *priv = &vif->priv;
+
+ if (priv->wilc_gtk[key_index]) {
+ kfree(priv->wilc_gtk[key_index]->key);
+ priv->wilc_gtk[key_index]->key = NULL;
+ kfree(priv->wilc_gtk[key_index]->seq);
+ priv->wilc_gtk[key_index]->seq = NULL;
+
+ kfree(priv->wilc_gtk[key_index]);
+ priv->wilc_gtk[key_index] = NULL;
+ }
+
+ if (priv->wilc_ptk[key_index]) {
+ kfree(priv->wilc_ptk[key_index]->key);
+ priv->wilc_ptk[key_index]->key = NULL;
+ kfree(priv->wilc_ptk[key_index]->seq);
+ priv->wilc_ptk[key_index]->seq = NULL;
+ kfree(priv->wilc_ptk[key_index]);
+ priv->wilc_ptk[key_index] = NULL;
+ }
+
+ if (key_index <= 3 && priv->wep_key_len[key_index]) {
+ memset(priv->wep_key[key_index], 0,
+ priv->wep_key_len[key_index]);
+ priv->wep_key_len[key_index] = 0;
+ wilc_remove_wep_key(vif, key_index);
+ }
+
+ return 0;
+}
+
+static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie, struct key_params *))
+{
+ struct wilc_vif *vif = netdev_priv(netdev);
+ struct wilc_priv *priv = &vif->priv;
+ struct key_params key_params;
+
+ if (!pairwise) {
+ 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;
+}
+
+static int set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool unicast, bool multicast)
+{
+ struct wilc_vif *vif = netdev_priv(netdev);
+
+ wilc_set_wep_default_keyid(vif, key_index);
+
+ return 0;
+}
+
+static int get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc_priv *priv = &vif->priv;
+ 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;
+
+ 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 change_bss(struct wiphy *wiphy, struct net_device *dev,
+ struct bss_parameters *params)
+{
+ return 0;
+}
+
+static int set_wiphy_params(struct wiphy *wiphy, 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,