162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries.
462306a36Sopenharmony_ci * All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "cfg80211.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define GO_NEG_REQ			0x00
1062306a36Sopenharmony_ci#define GO_NEG_RSP			0x01
1162306a36Sopenharmony_ci#define GO_NEG_CONF			0x02
1262306a36Sopenharmony_ci#define P2P_INV_REQ			0x03
1362306a36Sopenharmony_ci#define P2P_INV_RSP			0x04
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define WILC_INVALID_CHANNEL		0
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Operation at 2.4 GHz with channels 1-13 */
1862306a36Sopenharmony_ci#define WILC_WLAN_OPERATING_CLASS_2_4GHZ		0x51
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic const struct ieee80211_txrx_stypes
2162306a36Sopenharmony_ci	wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = {
2262306a36Sopenharmony_ci	[NL80211_IFTYPE_STATION] = {
2362306a36Sopenharmony_ci		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2462306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_AUTH >> 4),
2562306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2662306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
2762306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_AUTH >> 4)
2862306a36Sopenharmony_ci	},
2962306a36Sopenharmony_ci	[NL80211_IFTYPE_AP] = {
3062306a36Sopenharmony_ci		.tx = 0xffff,
3162306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
3262306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
3362306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
3462306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_DISASSOC >> 4) |
3562306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_AUTH >> 4) |
3662306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_DEAUTH >> 4) |
3762306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_ACTION >> 4)
3862306a36Sopenharmony_ci	},
3962306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_CLIENT] = {
4062306a36Sopenharmony_ci		.tx = 0xffff,
4162306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
4262306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
4362306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
4462306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
4562306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_DISASSOC >> 4) |
4662306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_AUTH >> 4) |
4762306a36Sopenharmony_ci			BIT(IEEE80211_STYPE_DEAUTH >> 4)
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#ifdef CONFIG_PM
5262306a36Sopenharmony_cistatic const struct wiphy_wowlan_support wowlan_support = {
5362306a36Sopenharmony_ci	.flags = WIPHY_WOWLAN_ANY
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct wilc_p2p_mgmt_data {
5862306a36Sopenharmony_ci	int size;
5962306a36Sopenharmony_ci	u8 *buff;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct wilc_p2p_pub_act_frame {
6362306a36Sopenharmony_ci	u8 category;
6462306a36Sopenharmony_ci	u8 action;
6562306a36Sopenharmony_ci	u8 oui[3];
6662306a36Sopenharmony_ci	u8 oui_type;
6762306a36Sopenharmony_ci	u8 oui_subtype;
6862306a36Sopenharmony_ci	u8 dialog_token;
6962306a36Sopenharmony_ci	u8 elem[];
7062306a36Sopenharmony_ci} __packed;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct wilc_vendor_specific_ie {
7362306a36Sopenharmony_ci	u8 tag_number;
7462306a36Sopenharmony_ci	u8 tag_len;
7562306a36Sopenharmony_ci	u8 oui[3];
7662306a36Sopenharmony_ci	u8 oui_type;
7762306a36Sopenharmony_ci	u8 attr[];
7862306a36Sopenharmony_ci} __packed;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct wilc_attr_entry {
8162306a36Sopenharmony_ci	u8  attr_type;
8262306a36Sopenharmony_ci	__le16 attr_len;
8362306a36Sopenharmony_ci	u8 val[];
8462306a36Sopenharmony_ci} __packed;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistruct wilc_attr_oper_ch {
8762306a36Sopenharmony_ci	u8 attr_type;
8862306a36Sopenharmony_ci	__le16 attr_len;
8962306a36Sopenharmony_ci	u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
9062306a36Sopenharmony_ci	u8 op_class;
9162306a36Sopenharmony_ci	u8 op_channel;
9262306a36Sopenharmony_ci} __packed;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct wilc_attr_ch_list {
9562306a36Sopenharmony_ci	u8 attr_type;
9662306a36Sopenharmony_ci	__le16 attr_len;
9762306a36Sopenharmony_ci	u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
9862306a36Sopenharmony_ci	u8 elem[];
9962306a36Sopenharmony_ci} __packed;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct wilc_ch_list_elem {
10262306a36Sopenharmony_ci	u8 op_class;
10362306a36Sopenharmony_ci	u8 no_of_channels;
10462306a36Sopenharmony_ci	u8 ch_list[];
10562306a36Sopenharmony_ci} __packed;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void cfg_scan_result(enum scan_event scan_event,
10862306a36Sopenharmony_ci			    struct wilc_rcvd_net_info *info, void *user_void)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct wilc_priv *priv = user_void;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!priv->cfg_scanning)
11362306a36Sopenharmony_ci		return;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
11662306a36Sopenharmony_ci		s32 freq;
11762306a36Sopenharmony_ci		struct ieee80211_channel *channel;
11862306a36Sopenharmony_ci		struct cfg80211_bss *bss;
11962306a36Sopenharmony_ci		struct wiphy *wiphy = priv->dev->ieee80211_ptr->wiphy;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		if (!wiphy || !info)
12262306a36Sopenharmony_ci			return;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		freq = ieee80211_channel_to_frequency((s32)info->ch,
12562306a36Sopenharmony_ci						      NL80211_BAND_2GHZ);
12662306a36Sopenharmony_ci		channel = ieee80211_get_channel(wiphy, freq);
12762306a36Sopenharmony_ci		if (!channel)
12862306a36Sopenharmony_ci			return;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		bss = cfg80211_inform_bss_frame(wiphy, channel, info->mgmt,
13162306a36Sopenharmony_ci						info->frame_len,
13262306a36Sopenharmony_ci						(s32)info->rssi * 100,
13362306a36Sopenharmony_ci						GFP_KERNEL);
13462306a36Sopenharmony_ci		cfg80211_put_bss(wiphy, bss);
13562306a36Sopenharmony_ci	} else if (scan_event == SCAN_EVENT_DONE) {
13662306a36Sopenharmony_ci		mutex_lock(&priv->scan_req_lock);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		if (priv->scan_req) {
13962306a36Sopenharmony_ci			struct cfg80211_scan_info info = {
14062306a36Sopenharmony_ci				.aborted = false,
14162306a36Sopenharmony_ci			};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci			cfg80211_scan_done(priv->scan_req, &info);
14462306a36Sopenharmony_ci			priv->cfg_scanning = false;
14562306a36Sopenharmony_ci			priv->scan_req = NULL;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci		mutex_unlock(&priv->scan_req_lock);
14862306a36Sopenharmony_ci	} else if (scan_event == SCAN_EVENT_ABORTED) {
14962306a36Sopenharmony_ci		mutex_lock(&priv->scan_req_lock);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		if (priv->scan_req) {
15262306a36Sopenharmony_ci			struct cfg80211_scan_info info = {
15362306a36Sopenharmony_ci				.aborted = false,
15462306a36Sopenharmony_ci			};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci			cfg80211_scan_done(priv->scan_req, &info);
15762306a36Sopenharmony_ci			priv->cfg_scanning = false;
15862306a36Sopenharmony_ci			priv->scan_req = NULL;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		mutex_unlock(&priv->scan_req_lock);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void cfg_connect_result(enum conn_event conn_disconn_evt, u8 mac_status,
16562306a36Sopenharmony_ci			       void *priv_data)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct wilc_priv *priv = priv_data;
16862306a36Sopenharmony_ci	struct net_device *dev = priv->dev;
16962306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
17062306a36Sopenharmony_ci	struct wilc *wl = vif->wilc;
17162306a36Sopenharmony_ci	struct host_if_drv *wfi_drv = priv->hif_drv;
17262306a36Sopenharmony_ci	struct wilc_conn_info *conn_info = &wfi_drv->conn_info;
17362306a36Sopenharmony_ci	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	vif->connecting = false;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (conn_disconn_evt == CONN_DISCONN_EVENT_CONN_RESP) {
17862306a36Sopenharmony_ci		u16 connect_status = conn_info->status;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		if (mac_status == WILC_MAC_STATUS_DISCONNECTED &&
18162306a36Sopenharmony_ci		    connect_status == WLAN_STATUS_SUCCESS) {
18262306a36Sopenharmony_ci			connect_status = WLAN_STATUS_UNSPECIFIED_FAILURE;
18362306a36Sopenharmony_ci			wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci			if (vif->iftype != WILC_CLIENT_MODE)
18662306a36Sopenharmony_ci				wl->sta_ch = WILC_INVALID_CHANNEL;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci			netdev_err(dev, "Unspecified failure\n");
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		if (connect_status == WLAN_STATUS_SUCCESS)
19262306a36Sopenharmony_ci			memcpy(priv->associated_bss, conn_info->bssid,
19362306a36Sopenharmony_ci			       ETH_ALEN);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		cfg80211_ref_bss(wiphy, vif->bss);
19662306a36Sopenharmony_ci		cfg80211_connect_bss(dev, conn_info->bssid, vif->bss,
19762306a36Sopenharmony_ci				     conn_info->req_ies,
19862306a36Sopenharmony_ci				     conn_info->req_ies_len,
19962306a36Sopenharmony_ci				     conn_info->resp_ies,
20062306a36Sopenharmony_ci				     conn_info->resp_ies_len,
20162306a36Sopenharmony_ci				     connect_status, GFP_KERNEL,
20262306a36Sopenharmony_ci				     NL80211_TIMEOUT_UNSPECIFIED);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		vif->bss = NULL;
20562306a36Sopenharmony_ci	} else if (conn_disconn_evt == CONN_DISCONN_EVENT_DISCONN_NOTIF) {
20662306a36Sopenharmony_ci		u16 reason = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		eth_zero_addr(priv->associated_bss);
20962306a36Sopenharmony_ci		wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		if (vif->iftype != WILC_CLIENT_MODE) {
21262306a36Sopenharmony_ci			wl->sta_ch = WILC_INVALID_CHANNEL;
21362306a36Sopenharmony_ci		} else {
21462306a36Sopenharmony_ci			if (wfi_drv->ifc_up)
21562306a36Sopenharmony_ci				reason = 3;
21662306a36Sopenharmony_ci			else
21762306a36Sopenharmony_ci				reason = 1;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		cfg80211_disconnected(dev, reason, NULL, 0, false, GFP_KERNEL);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistruct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct wilc_vif *vif;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	vif = list_first_or_null_rcu(&wl->vif_list, typeof(*vif), list);
22962306a36Sopenharmony_ci	if (!vif)
23062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return vif;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int set_channel(struct wiphy *wiphy,
23662306a36Sopenharmony_ci		       struct cfg80211_chan_def *chandef)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
23962306a36Sopenharmony_ci	struct wilc_vif *vif;
24062306a36Sopenharmony_ci	u32 channelnum;
24162306a36Sopenharmony_ci	int result;
24262306a36Sopenharmony_ci	int srcu_idx;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	srcu_idx = srcu_read_lock(&wl->srcu);
24562306a36Sopenharmony_ci	vif = wilc_get_wl_to_vif(wl);
24662306a36Sopenharmony_ci	if (IS_ERR(vif)) {
24762306a36Sopenharmony_ci		srcu_read_unlock(&wl->srcu, srcu_idx);
24862306a36Sopenharmony_ci		return PTR_ERR(vif);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	channelnum = ieee80211_frequency_to_channel(chandef->chan->center_freq);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	wl->op_ch = channelnum;
25462306a36Sopenharmony_ci	result = wilc_set_mac_chnl_num(vif, channelnum);
25562306a36Sopenharmony_ci	if (result)
25662306a36Sopenharmony_ci		netdev_err(vif->ndev, "Error in setting channel\n");
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	srcu_read_unlock(&wl->srcu, srcu_idx);
25962306a36Sopenharmony_ci	return result;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(request->wdev->netdev);
26562306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
26662306a36Sopenharmony_ci	u32 i;
26762306a36Sopenharmony_ci	int ret = 0;
26862306a36Sopenharmony_ci	u8 scan_ch_list[WILC_MAX_NUM_SCANNED_CH];
26962306a36Sopenharmony_ci	u8 scan_type;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (request->n_channels > WILC_MAX_NUM_SCANNED_CH) {
27262306a36Sopenharmony_ci		netdev_err(vif->ndev, "Requested scanned channels over\n");
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	priv->scan_req = request;
27762306a36Sopenharmony_ci	priv->cfg_scanning = true;
27862306a36Sopenharmony_ci	for (i = 0; i < request->n_channels; i++) {
27962306a36Sopenharmony_ci		u16 freq = request->channels[i]->center_freq;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		scan_ch_list[i] = ieee80211_frequency_to_channel(freq);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (request->n_ssids)
28562306a36Sopenharmony_ci		scan_type = WILC_FW_ACTIVE_SCAN;
28662306a36Sopenharmony_ci	else
28762306a36Sopenharmony_ci		scan_type = WILC_FW_PASSIVE_SCAN;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	ret = wilc_scan(vif, WILC_FW_USER_SCAN, scan_type, scan_ch_list,
29062306a36Sopenharmony_ci			request->n_channels, cfg_scan_result, (void *)priv,
29162306a36Sopenharmony_ci			request);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (ret) {
29462306a36Sopenharmony_ci		priv->scan_req = NULL;
29562306a36Sopenharmony_ci		priv->cfg_scanning = false;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return ret;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int connect(struct wiphy *wiphy, struct net_device *dev,
30262306a36Sopenharmony_ci		   struct cfg80211_connect_params *sme)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
30562306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
30662306a36Sopenharmony_ci	struct host_if_drv *wfi_drv = priv->hif_drv;
30762306a36Sopenharmony_ci	int ret;
30862306a36Sopenharmony_ci	u32 i;
30962306a36Sopenharmony_ci	u8 security = WILC_FW_SEC_NO;
31062306a36Sopenharmony_ci	enum mfptype mfp_type = WILC_FW_MFP_NONE;
31162306a36Sopenharmony_ci	enum authtype auth_type = WILC_FW_AUTH_ANY;
31262306a36Sopenharmony_ci	u32 cipher_group;
31362306a36Sopenharmony_ci	struct cfg80211_bss *bss;
31462306a36Sopenharmony_ci	void *join_params;
31562306a36Sopenharmony_ci	u8 ch;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	vif->connecting = true;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	cipher_group = sme->crypto.cipher_group;
32062306a36Sopenharmony_ci	if (cipher_group != 0) {
32162306a36Sopenharmony_ci		if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
32262306a36Sopenharmony_ci			if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
32362306a36Sopenharmony_ci				security = WILC_FW_SEC_WPA2_TKIP;
32462306a36Sopenharmony_ci			else
32562306a36Sopenharmony_ci				security = WILC_FW_SEC_WPA2_AES;
32662306a36Sopenharmony_ci		} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
32762306a36Sopenharmony_ci			if (cipher_group == WLAN_CIPHER_SUITE_TKIP)
32862306a36Sopenharmony_ci				security = WILC_FW_SEC_WPA_TKIP;
32962306a36Sopenharmony_ci			else
33062306a36Sopenharmony_ci				security = WILC_FW_SEC_WPA_AES;
33162306a36Sopenharmony_ci		} else {
33262306a36Sopenharmony_ci			ret = -ENOTSUPP;
33362306a36Sopenharmony_ci			netdev_err(dev, "%s: Unsupported cipher\n",
33462306a36Sopenharmony_ci				   __func__);
33562306a36Sopenharmony_ci			goto out_error;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if ((sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) ||
34062306a36Sopenharmony_ci	    (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) {
34162306a36Sopenharmony_ci		for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) {
34262306a36Sopenharmony_ci			u32 ciphers_pairwise = sme->crypto.ciphers_pairwise[i];
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci			if (ciphers_pairwise == WLAN_CIPHER_SUITE_TKIP)
34562306a36Sopenharmony_ci				security |= WILC_FW_TKIP;
34662306a36Sopenharmony_ci			else
34762306a36Sopenharmony_ci				security |= WILC_FW_AES;
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	switch (sme->auth_type) {
35262306a36Sopenharmony_ci	case NL80211_AUTHTYPE_OPEN_SYSTEM:
35362306a36Sopenharmony_ci		auth_type = WILC_FW_AUTH_OPEN_SYSTEM;
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	case NL80211_AUTHTYPE_SAE:
35762306a36Sopenharmony_ci		auth_type = WILC_FW_AUTH_SAE;
35862306a36Sopenharmony_ci		if (sme->ssid_len) {
35962306a36Sopenharmony_ci			memcpy(vif->auth.ssid.ssid, sme->ssid, sme->ssid_len);
36062306a36Sopenharmony_ci			vif->auth.ssid.ssid_len = sme->ssid_len;
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci		vif->auth.key_mgmt_suite = cpu_to_be32(sme->crypto.akm_suites[0]);
36362306a36Sopenharmony_ci		ether_addr_copy(vif->auth.bssid, sme->bssid);
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	default:
36762306a36Sopenharmony_ci		break;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (sme->crypto.n_akm_suites) {
37162306a36Sopenharmony_ci		if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X)
37262306a36Sopenharmony_ci			auth_type = WILC_FW_AUTH_IEEE8021;
37362306a36Sopenharmony_ci		else if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_PSK_SHA256)
37462306a36Sopenharmony_ci			auth_type = WILC_FW_AUTH_OPEN_SYSTEM_SHA256;
37562306a36Sopenharmony_ci		else if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X_SHA256)
37662306a36Sopenharmony_ci			auth_type = WILC_FW_AUTH_IEE8021X_SHA256;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (wfi_drv->usr_scan_req.scan_result) {
38062306a36Sopenharmony_ci		netdev_err(vif->ndev, "%s: Scan in progress\n", __func__);
38162306a36Sopenharmony_ci		ret = -EBUSY;
38262306a36Sopenharmony_ci		goto out_error;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid,
38662306a36Sopenharmony_ci			       sme->ssid_len, IEEE80211_BSS_TYPE_ANY,
38762306a36Sopenharmony_ci			       IEEE80211_PRIVACY(sme->privacy));
38862306a36Sopenharmony_ci	if (!bss) {
38962306a36Sopenharmony_ci		ret = -EINVAL;
39062306a36Sopenharmony_ci		goto out_error;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (ether_addr_equal_unaligned(vif->bssid, bss->bssid)) {
39462306a36Sopenharmony_ci		ret = -EALREADY;
39562306a36Sopenharmony_ci		goto out_put_bss;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	join_params = wilc_parse_join_bss_param(bss, &sme->crypto);
39962306a36Sopenharmony_ci	if (!join_params) {
40062306a36Sopenharmony_ci		netdev_err(dev, "%s: failed to construct join param\n",
40162306a36Sopenharmony_ci			   __func__);
40262306a36Sopenharmony_ci		ret = -EINVAL;
40362306a36Sopenharmony_ci		goto out_put_bss;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
40762306a36Sopenharmony_ci	vif->wilc->op_ch = ch;
40862306a36Sopenharmony_ci	if (vif->iftype != WILC_CLIENT_MODE)
40962306a36Sopenharmony_ci		vif->wilc->sta_ch = ch;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	wilc_wlan_set_bssid(dev, bss->bssid, WILC_STATION_MODE);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	wfi_drv->conn_info.security = security;
41462306a36Sopenharmony_ci	wfi_drv->conn_info.auth_type = auth_type;
41562306a36Sopenharmony_ci	wfi_drv->conn_info.ch = ch;
41662306a36Sopenharmony_ci	wfi_drv->conn_info.conn_result = cfg_connect_result;
41762306a36Sopenharmony_ci	wfi_drv->conn_info.arg = priv;
41862306a36Sopenharmony_ci	wfi_drv->conn_info.param = join_params;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (sme->mfp == NL80211_MFP_OPTIONAL)
42162306a36Sopenharmony_ci		mfp_type = WILC_FW_MFP_OPTIONAL;
42262306a36Sopenharmony_ci	else if (sme->mfp == NL80211_MFP_REQUIRED)
42362306a36Sopenharmony_ci		mfp_type = WILC_FW_MFP_REQUIRED;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	wfi_drv->conn_info.mfp_type = mfp_type;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ret = wilc_set_join_req(vif, bss->bssid, sme->ie, sme->ie_len);
42862306a36Sopenharmony_ci	if (ret) {
42962306a36Sopenharmony_ci		netdev_err(dev, "wilc_set_join_req(): Error\n");
43062306a36Sopenharmony_ci		ret = -ENOENT;
43162306a36Sopenharmony_ci		if (vif->iftype != WILC_CLIENT_MODE)
43262306a36Sopenharmony_ci			vif->wilc->sta_ch = WILC_INVALID_CHANNEL;
43362306a36Sopenharmony_ci		wilc_wlan_set_bssid(dev, NULL, WILC_STATION_MODE);
43462306a36Sopenharmony_ci		wfi_drv->conn_info.conn_result = NULL;
43562306a36Sopenharmony_ci		kfree(join_params);
43662306a36Sopenharmony_ci		goto out_put_bss;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	kfree(join_params);
43962306a36Sopenharmony_ci	vif->bss = bss;
44062306a36Sopenharmony_ci	cfg80211_put_bss(wiphy, bss);
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciout_put_bss:
44462306a36Sopenharmony_ci	cfg80211_put_bss(wiphy, bss);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ciout_error:
44762306a36Sopenharmony_ci	vif->connecting = false;
44862306a36Sopenharmony_ci	return ret;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int disconnect(struct wiphy *wiphy, struct net_device *dev,
45262306a36Sopenharmony_ci		      u16 reason_code)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
45562306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
45662306a36Sopenharmony_ci	struct wilc *wilc = vif->wilc;
45762306a36Sopenharmony_ci	int ret;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	vif->connecting = false;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (!wilc)
46262306a36Sopenharmony_ci		return -EIO;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (wilc->close) {
46562306a36Sopenharmony_ci		/* already disconnected done */
46662306a36Sopenharmony_ci		cfg80211_disconnected(dev, 0, NULL, 0, true, GFP_KERNEL);
46762306a36Sopenharmony_ci		return 0;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (vif->iftype != WILC_CLIENT_MODE)
47162306a36Sopenharmony_ci		wilc->sta_ch = WILC_INVALID_CHANNEL;
47262306a36Sopenharmony_ci	wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	priv->hif_drv->p2p_timeout = 0;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ret = wilc_disconnect(vif);
47762306a36Sopenharmony_ci	if (ret != 0) {
47862306a36Sopenharmony_ci		netdev_err(priv->dev, "Error in disconnecting\n");
47962306a36Sopenharmony_ci		ret = -EINVAL;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	vif->bss = NULL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return ret;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int wilc_wfi_cfg_allocate_wpa_entry(struct wilc_priv *priv, u8 idx)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	if (!priv->wilc_gtk[idx]) {
49062306a36Sopenharmony_ci		priv->wilc_gtk[idx] = kzalloc(sizeof(*priv->wilc_gtk[idx]),
49162306a36Sopenharmony_ci					      GFP_KERNEL);
49262306a36Sopenharmony_ci		if (!priv->wilc_gtk[idx])
49362306a36Sopenharmony_ci			return -ENOMEM;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!priv->wilc_ptk[idx]) {
49762306a36Sopenharmony_ci		priv->wilc_ptk[idx] = kzalloc(sizeof(*priv->wilc_ptk[idx]),
49862306a36Sopenharmony_ci					      GFP_KERNEL);
49962306a36Sopenharmony_ci		if (!priv->wilc_ptk[idx])
50062306a36Sopenharmony_ci			return -ENOMEM;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int wilc_wfi_cfg_allocate_wpa_igtk_entry(struct wilc_priv *priv, u8 idx)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	idx -= 4;
50962306a36Sopenharmony_ci	if (!priv->wilc_igtk[idx]) {
51062306a36Sopenharmony_ci		priv->wilc_igtk[idx] = kzalloc(sizeof(*priv->wilc_igtk[idx]),
51162306a36Sopenharmony_ci					       GFP_KERNEL);
51262306a36Sopenharmony_ci		if (!priv->wilc_igtk[idx])
51362306a36Sopenharmony_ci			return -ENOMEM;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int wilc_wfi_cfg_copy_wpa_info(struct wilc_wfi_key *key_info,
51962306a36Sopenharmony_ci				      struct key_params *params)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	kfree(key_info->key);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	key_info->key = kmemdup(params->key, params->key_len, GFP_KERNEL);
52462306a36Sopenharmony_ci	if (!key_info->key)
52562306a36Sopenharmony_ci		return -ENOMEM;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	kfree(key_info->seq);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (params->seq_len > 0) {
53062306a36Sopenharmony_ci		key_info->seq = kmemdup(params->seq, params->seq_len,
53162306a36Sopenharmony_ci					GFP_KERNEL);
53262306a36Sopenharmony_ci		if (!key_info->seq)
53362306a36Sopenharmony_ci			return -ENOMEM;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	key_info->cipher = params->cipher;
53762306a36Sopenharmony_ci	key_info->key_len = params->key_len;
53862306a36Sopenharmony_ci	key_info->seq_len = params->seq_len;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int add_key(struct wiphy *wiphy, struct net_device *netdev, int link_id,
54462306a36Sopenharmony_ci		   u8 key_index, bool pairwise, const u8 *mac_addr,
54562306a36Sopenharmony_ci		   struct key_params *params)
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	int ret = 0, keylen = params->key_len;
54962306a36Sopenharmony_ci	const u8 *rx_mic = NULL;
55062306a36Sopenharmony_ci	const u8 *tx_mic = NULL;
55162306a36Sopenharmony_ci	u8 mode = WILC_FW_SEC_NO;
55262306a36Sopenharmony_ci	u8 op_mode;
55362306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
55462306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
55562306a36Sopenharmony_ci	struct wilc_wfi_key *key;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	switch (params->cipher) {
55862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
55962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
56062306a36Sopenharmony_ci		if (priv->wdev.iftype == NL80211_IFTYPE_AP ||
56162306a36Sopenharmony_ci		    priv->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
56262306a36Sopenharmony_ci			struct wilc_wfi_key *key;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci			ret = wilc_wfi_cfg_allocate_wpa_entry(priv, key_index);
56562306a36Sopenharmony_ci			if (ret)
56662306a36Sopenharmony_ci				return -ENOMEM;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			if (params->key_len > 16 &&
56962306a36Sopenharmony_ci			    params->cipher == WLAN_CIPHER_SUITE_TKIP) {
57062306a36Sopenharmony_ci				tx_mic = params->key + 24;
57162306a36Sopenharmony_ci				rx_mic = params->key + 16;
57262306a36Sopenharmony_ci				keylen = params->key_len - 16;
57362306a36Sopenharmony_ci			}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci			if (!pairwise) {
57662306a36Sopenharmony_ci				if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
57762306a36Sopenharmony_ci					mode = WILC_FW_SEC_WPA_TKIP;
57862306a36Sopenharmony_ci				else
57962306a36Sopenharmony_ci					mode = WILC_FW_SEC_WPA2_AES;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci				priv->wilc_groupkey = mode;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci				key = priv->wilc_gtk[key_index];
58462306a36Sopenharmony_ci			} else {
58562306a36Sopenharmony_ci				if (params->cipher == WLAN_CIPHER_SUITE_TKIP)
58662306a36Sopenharmony_ci					mode = WILC_FW_SEC_WPA_TKIP;
58762306a36Sopenharmony_ci				else
58862306a36Sopenharmony_ci					mode = priv->wilc_groupkey | WILC_FW_AES;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci				key = priv->wilc_ptk[key_index];
59162306a36Sopenharmony_ci			}
59262306a36Sopenharmony_ci			ret = wilc_wfi_cfg_copy_wpa_info(key, params);
59362306a36Sopenharmony_ci			if (ret)
59462306a36Sopenharmony_ci				return -ENOMEM;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci			op_mode = WILC_AP_MODE;
59762306a36Sopenharmony_ci		} else {
59862306a36Sopenharmony_ci			if (params->key_len > 16 &&
59962306a36Sopenharmony_ci			    params->cipher == WLAN_CIPHER_SUITE_TKIP) {
60062306a36Sopenharmony_ci				rx_mic = params->key + 24;
60162306a36Sopenharmony_ci				tx_mic = params->key + 16;
60262306a36Sopenharmony_ci				keylen = params->key_len - 16;
60362306a36Sopenharmony_ci			}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci			op_mode = WILC_STATION_MODE;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		if (!pairwise)
60962306a36Sopenharmony_ci			ret = wilc_add_rx_gtk(vif, params->key, keylen,
61062306a36Sopenharmony_ci					      key_index, params->seq_len,
61162306a36Sopenharmony_ci					      params->seq, rx_mic, tx_mic,
61262306a36Sopenharmony_ci					      op_mode, mode);
61362306a36Sopenharmony_ci		else
61462306a36Sopenharmony_ci			ret = wilc_add_ptk(vif, params->key, keylen, mac_addr,
61562306a36Sopenharmony_ci					   rx_mic, tx_mic, op_mode, mode,
61662306a36Sopenharmony_ci					   key_index);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_AES_CMAC:
62062306a36Sopenharmony_ci		ret = wilc_wfi_cfg_allocate_wpa_igtk_entry(priv, key_index);
62162306a36Sopenharmony_ci		if (ret)
62262306a36Sopenharmony_ci			return -ENOMEM;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		key = priv->wilc_igtk[key_index - 4];
62562306a36Sopenharmony_ci		ret = wilc_wfi_cfg_copy_wpa_info(key, params);
62662306a36Sopenharmony_ci		if (ret)
62762306a36Sopenharmony_ci			return -ENOMEM;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		if (priv->wdev.iftype == NL80211_IFTYPE_AP ||
63062306a36Sopenharmony_ci		    priv->wdev.iftype == NL80211_IFTYPE_P2P_GO)
63162306a36Sopenharmony_ci			op_mode = WILC_AP_MODE;
63262306a36Sopenharmony_ci		else
63362306a36Sopenharmony_ci			op_mode = WILC_STATION_MODE;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		ret = wilc_add_igtk(vif, params->key, keylen, params->seq,
63662306a36Sopenharmony_ci				    params->seq_len, mac_addr, op_mode,
63762306a36Sopenharmony_ci				    key_index);
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	default:
64162306a36Sopenharmony_ci		netdev_err(netdev, "%s: Unsupported cipher\n", __func__);
64262306a36Sopenharmony_ci		ret = -ENOTSUPP;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	return ret;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic int del_key(struct wiphy *wiphy, struct net_device *netdev, int link_id,
64962306a36Sopenharmony_ci		   u8 key_index,
65062306a36Sopenharmony_ci		   bool pairwise,
65162306a36Sopenharmony_ci		   const u8 *mac_addr)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
65462306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (!pairwise && (key_index == 4 || key_index == 5)) {
65762306a36Sopenharmony_ci		key_index -= 4;
65862306a36Sopenharmony_ci		if (priv->wilc_igtk[key_index]) {
65962306a36Sopenharmony_ci			kfree(priv->wilc_igtk[key_index]->key);
66062306a36Sopenharmony_ci			priv->wilc_igtk[key_index]->key = NULL;
66162306a36Sopenharmony_ci			kfree(priv->wilc_igtk[key_index]->seq);
66262306a36Sopenharmony_ci			priv->wilc_igtk[key_index]->seq = NULL;
66362306a36Sopenharmony_ci			kfree(priv->wilc_igtk[key_index]);
66462306a36Sopenharmony_ci			priv->wilc_igtk[key_index] = NULL;
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci	} else {
66762306a36Sopenharmony_ci		if (priv->wilc_gtk[key_index]) {
66862306a36Sopenharmony_ci			kfree(priv->wilc_gtk[key_index]->key);
66962306a36Sopenharmony_ci			priv->wilc_gtk[key_index]->key = NULL;
67062306a36Sopenharmony_ci			kfree(priv->wilc_gtk[key_index]->seq);
67162306a36Sopenharmony_ci			priv->wilc_gtk[key_index]->seq = NULL;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci			kfree(priv->wilc_gtk[key_index]);
67462306a36Sopenharmony_ci			priv->wilc_gtk[key_index] = NULL;
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		if (priv->wilc_ptk[key_index]) {
67762306a36Sopenharmony_ci			kfree(priv->wilc_ptk[key_index]->key);
67862306a36Sopenharmony_ci			priv->wilc_ptk[key_index]->key = NULL;
67962306a36Sopenharmony_ci			kfree(priv->wilc_ptk[key_index]->seq);
68062306a36Sopenharmony_ci			priv->wilc_ptk[key_index]->seq = NULL;
68162306a36Sopenharmony_ci			kfree(priv->wilc_ptk[key_index]);
68262306a36Sopenharmony_ci			priv->wilc_ptk[key_index] = NULL;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic int get_key(struct wiphy *wiphy, struct net_device *netdev, int link_id,
69062306a36Sopenharmony_ci		   u8 key_index, bool pairwise, const u8 *mac_addr,
69162306a36Sopenharmony_ci		   void *cookie,
69262306a36Sopenharmony_ci		   void (*callback)(void *cookie, struct key_params *))
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
69562306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
69662306a36Sopenharmony_ci	struct  key_params key_params;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (!pairwise) {
69962306a36Sopenharmony_ci		if (key_index == 4 || key_index == 5) {
70062306a36Sopenharmony_ci			key_index -= 4;
70162306a36Sopenharmony_ci			key_params.key = priv->wilc_igtk[key_index]->key;
70262306a36Sopenharmony_ci			key_params.cipher = priv->wilc_igtk[key_index]->cipher;
70362306a36Sopenharmony_ci			key_params.key_len = priv->wilc_igtk[key_index]->key_len;
70462306a36Sopenharmony_ci			key_params.seq = priv->wilc_igtk[key_index]->seq;
70562306a36Sopenharmony_ci			key_params.seq_len = priv->wilc_igtk[key_index]->seq_len;
70662306a36Sopenharmony_ci		} else {
70762306a36Sopenharmony_ci			key_params.key = priv->wilc_gtk[key_index]->key;
70862306a36Sopenharmony_ci			key_params.cipher = priv->wilc_gtk[key_index]->cipher;
70962306a36Sopenharmony_ci			key_params.key_len = priv->wilc_gtk[key_index]->key_len;
71062306a36Sopenharmony_ci			key_params.seq = priv->wilc_gtk[key_index]->seq;
71162306a36Sopenharmony_ci			key_params.seq_len = priv->wilc_gtk[key_index]->seq_len;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci	} else {
71462306a36Sopenharmony_ci		key_params.key = priv->wilc_ptk[key_index]->key;
71562306a36Sopenharmony_ci		key_params.cipher = priv->wilc_ptk[key_index]->cipher;
71662306a36Sopenharmony_ci		key_params.key_len = priv->wilc_ptk[key_index]->key_len;
71762306a36Sopenharmony_ci		key_params.seq = priv->wilc_ptk[key_index]->seq;
71862306a36Sopenharmony_ci		key_params.seq_len = priv->wilc_ptk[key_index]->seq_len;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	callback(cookie, &key_params);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci/* wiphy_new_nm() will WARNON if not present */
72762306a36Sopenharmony_cistatic int set_default_key(struct wiphy *wiphy, struct net_device *netdev,
72862306a36Sopenharmony_ci			   int link_id, u8 key_index, bool unicast,
72962306a36Sopenharmony_ci			   bool multicast)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	return 0;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic int set_default_mgmt_key(struct wiphy *wiphy, struct net_device *netdev,
73562306a36Sopenharmony_ci				int link_id, u8 key_index)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	return wilc_set_default_mgmt_key_index(vif, key_index);
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic int get_station(struct wiphy *wiphy, struct net_device *dev,
74362306a36Sopenharmony_ci		       const u8 *mac, struct station_info *sinfo)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
74662306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
74762306a36Sopenharmony_ci	struct wilc *wilc = vif->wilc;
74862306a36Sopenharmony_ci	u32 i = 0;
74962306a36Sopenharmony_ci	u32 associatedsta = ~0;
75062306a36Sopenharmony_ci	u32 inactive_time = 0;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
75362306a36Sopenharmony_ci		for (i = 0; i < NUM_STA_ASSOCIATED; i++) {
75462306a36Sopenharmony_ci			if (!(memcmp(mac,
75562306a36Sopenharmony_ci				     priv->assoc_stainfo.sta_associated_bss[i],
75662306a36Sopenharmony_ci				     ETH_ALEN))) {
75762306a36Sopenharmony_ci				associatedsta = i;
75862306a36Sopenharmony_ci				break;
75962306a36Sopenharmony_ci			}
76062306a36Sopenharmony_ci		}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci		if (associatedsta == ~0) {
76362306a36Sopenharmony_ci			netdev_err(dev, "sta required is not associated\n");
76462306a36Sopenharmony_ci			return -ENOENT;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		wilc_get_inactive_time(vif, mac, &inactive_time);
77062306a36Sopenharmony_ci		sinfo->inactive_time = 1000 * inactive_time;
77162306a36Sopenharmony_ci	} else if (vif->iftype == WILC_STATION_MODE) {
77262306a36Sopenharmony_ci		struct rf_info stats;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		if (!wilc->initialized)
77562306a36Sopenharmony_ci			return -EBUSY;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		wilc_get_statistics(vif, &stats);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL) |
78062306a36Sopenharmony_ci				 BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
78162306a36Sopenharmony_ci				 BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
78262306a36Sopenharmony_ci				 BIT_ULL(NL80211_STA_INFO_TX_FAILED) |
78362306a36Sopenharmony_ci				 BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		sinfo->signal = stats.rssi;
78662306a36Sopenharmony_ci		sinfo->rx_packets = stats.rx_cnt;
78762306a36Sopenharmony_ci		sinfo->tx_packets = stats.tx_cnt + stats.tx_fail_cnt;
78862306a36Sopenharmony_ci		sinfo->tx_failed = stats.tx_fail_cnt;
78962306a36Sopenharmony_ci		sinfo->txrate.legacy = stats.link_speed * 10;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		if (stats.link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH &&
79262306a36Sopenharmony_ci		    stats.link_speed != DEFAULT_LINK_SPEED)
79362306a36Sopenharmony_ci			wilc_enable_tcp_ack_filter(vif, true);
79462306a36Sopenharmony_ci		else if (stats.link_speed != DEFAULT_LINK_SPEED)
79562306a36Sopenharmony_ci			wilc_enable_tcp_ack_filter(vif, false);
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci	return 0;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int change_bss(struct wiphy *wiphy, struct net_device *dev,
80162306a36Sopenharmony_ci		      struct bss_parameters *params)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	return 0;
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int set_wiphy_params(struct wiphy *wiphy, u32 changed)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	int ret = -EINVAL;
80962306a36Sopenharmony_ci	struct cfg_param_attr cfg_param_val;
81062306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
81162306a36Sopenharmony_ci	struct wilc_vif *vif;
81262306a36Sopenharmony_ci	struct wilc_priv *priv;
81362306a36Sopenharmony_ci	int srcu_idx;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	srcu_idx = srcu_read_lock(&wl->srcu);
81662306a36Sopenharmony_ci	vif = wilc_get_wl_to_vif(wl);
81762306a36Sopenharmony_ci	if (IS_ERR(vif))
81862306a36Sopenharmony_ci		goto out;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	priv = &vif->priv;
82162306a36Sopenharmony_ci	cfg_param_val.flag = 0;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_SHORT) {
82462306a36Sopenharmony_ci		netdev_dbg(vif->ndev,
82562306a36Sopenharmony_ci			   "Setting WIPHY_PARAM_RETRY_SHORT %d\n",
82662306a36Sopenharmony_ci			   wiphy->retry_short);
82762306a36Sopenharmony_ci		cfg_param_val.flag  |= WILC_CFG_PARAM_RETRY_SHORT;
82862306a36Sopenharmony_ci		cfg_param_val.short_retry_limit = wiphy->retry_short;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_LONG) {
83162306a36Sopenharmony_ci		netdev_dbg(vif->ndev,
83262306a36Sopenharmony_ci			   "Setting WIPHY_PARAM_RETRY_LONG %d\n",
83362306a36Sopenharmony_ci			   wiphy->retry_long);
83462306a36Sopenharmony_ci		cfg_param_val.flag |= WILC_CFG_PARAM_RETRY_LONG;
83562306a36Sopenharmony_ci		cfg_param_val.long_retry_limit = wiphy->retry_long;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
83862306a36Sopenharmony_ci		if (wiphy->frag_threshold > 255 &&
83962306a36Sopenharmony_ci		    wiphy->frag_threshold < 7937) {
84062306a36Sopenharmony_ci			netdev_dbg(vif->ndev,
84162306a36Sopenharmony_ci				   "Setting WIPHY_PARAM_FRAG_THRESHOLD %d\n",
84262306a36Sopenharmony_ci				   wiphy->frag_threshold);
84362306a36Sopenharmony_ci			cfg_param_val.flag |= WILC_CFG_PARAM_FRAG_THRESHOLD;
84462306a36Sopenharmony_ci			cfg_param_val.frag_threshold = wiphy->frag_threshold;
84562306a36Sopenharmony_ci		} else {
84662306a36Sopenharmony_ci			netdev_err(vif->ndev,
84762306a36Sopenharmony_ci				   "Fragmentation threshold out of range\n");
84862306a36Sopenharmony_ci			goto out;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
85362306a36Sopenharmony_ci		if (wiphy->rts_threshold > 255) {
85462306a36Sopenharmony_ci			netdev_dbg(vif->ndev,
85562306a36Sopenharmony_ci				   "Setting WIPHY_PARAM_RTS_THRESHOLD %d\n",
85662306a36Sopenharmony_ci				   wiphy->rts_threshold);
85762306a36Sopenharmony_ci			cfg_param_val.flag |= WILC_CFG_PARAM_RTS_THRESHOLD;
85862306a36Sopenharmony_ci			cfg_param_val.rts_threshold = wiphy->rts_threshold;
85962306a36Sopenharmony_ci		} else {
86062306a36Sopenharmony_ci			netdev_err(vif->ndev, "RTS threshold out of range\n");
86162306a36Sopenharmony_ci			goto out;
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	ret = wilc_hif_set_cfg(vif, &cfg_param_val);
86662306a36Sopenharmony_ci	if (ret)
86762306a36Sopenharmony_ci		netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n");
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ciout:
87062306a36Sopenharmony_ci	srcu_read_unlock(&wl->srcu, srcu_idx);
87162306a36Sopenharmony_ci	return ret;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
87562306a36Sopenharmony_ci		     struct cfg80211_pmksa *pmksa)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
87862306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
87962306a36Sopenharmony_ci	u32 i;
88062306a36Sopenharmony_ci	int ret = 0;
88162306a36Sopenharmony_ci	u8 flag = 0;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	for (i = 0; i < priv->pmkid_list.numpmkid; i++)	{
88462306a36Sopenharmony_ci		if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
88562306a36Sopenharmony_ci			    ETH_ALEN)) {
88662306a36Sopenharmony_ci			flag = PMKID_FOUND;
88762306a36Sopenharmony_ci			break;
88862306a36Sopenharmony_ci		}
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci	if (i < WILC_MAX_NUM_PMKIDS) {
89162306a36Sopenharmony_ci		memcpy(priv->pmkid_list.pmkidlist[i].bssid, pmksa->bssid,
89262306a36Sopenharmony_ci		       ETH_ALEN);
89362306a36Sopenharmony_ci		memcpy(priv->pmkid_list.pmkidlist[i].pmkid, pmksa->pmkid,
89462306a36Sopenharmony_ci		       WLAN_PMKID_LEN);
89562306a36Sopenharmony_ci		if (!(flag == PMKID_FOUND))
89662306a36Sopenharmony_ci			priv->pmkid_list.numpmkid++;
89762306a36Sopenharmony_ci	} else {
89862306a36Sopenharmony_ci		netdev_err(netdev, "Invalid PMKID index\n");
89962306a36Sopenharmony_ci		ret = -EINVAL;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (!ret)
90362306a36Sopenharmony_ci		ret = wilc_set_pmkid_info(vif, &priv->pmkid_list);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return ret;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic int del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
90962306a36Sopenharmony_ci		     struct cfg80211_pmksa *pmksa)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	u32 i;
91262306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
91362306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	for (i = 0; i < priv->pmkid_list.numpmkid; i++)	{
91662306a36Sopenharmony_ci		if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
91762306a36Sopenharmony_ci			    ETH_ALEN)) {
91862306a36Sopenharmony_ci			memset(&priv->pmkid_list.pmkidlist[i], 0,
91962306a36Sopenharmony_ci			       sizeof(struct wilc_pmkid));
92062306a36Sopenharmony_ci			break;
92162306a36Sopenharmony_ci		}
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (i == priv->pmkid_list.numpmkid)
92562306a36Sopenharmony_ci		return -EINVAL;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	for (; i < (priv->pmkid_list.numpmkid - 1); i++) {
92862306a36Sopenharmony_ci		memcpy(priv->pmkid_list.pmkidlist[i].bssid,
92962306a36Sopenharmony_ci		       priv->pmkid_list.pmkidlist[i + 1].bssid,
93062306a36Sopenharmony_ci		       ETH_ALEN);
93162306a36Sopenharmony_ci		memcpy(priv->pmkid_list.pmkidlist[i].pmkid,
93262306a36Sopenharmony_ci		       priv->pmkid_list.pmkidlist[i + 1].pmkid,
93362306a36Sopenharmony_ci		       WLAN_PMKID_LEN);
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci	priv->pmkid_list.numpmkid--;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	return 0;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(netdev);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	memset(&vif->priv.pmkid_list, 0, sizeof(struct wilc_pmkid_attr));
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	return 0;
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic inline void wilc_wfi_cfg_parse_ch_attr(u8 *buf, u32 len, u8 sta_ch)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct wilc_attr_entry *e;
95262306a36Sopenharmony_ci	struct wilc_attr_ch_list *ch_list;
95362306a36Sopenharmony_ci	struct wilc_attr_oper_ch *op_ch;
95462306a36Sopenharmony_ci	u32 index = 0;
95562306a36Sopenharmony_ci	u8 ch_list_idx = 0;
95662306a36Sopenharmony_ci	u8 op_ch_idx = 0;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (sta_ch == WILC_INVALID_CHANNEL)
95962306a36Sopenharmony_ci		return;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	while (index + sizeof(*e) <= len) {
96262306a36Sopenharmony_ci		u16 attr_size;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		e = (struct wilc_attr_entry *)&buf[index];
96562306a36Sopenharmony_ci		attr_size = le16_to_cpu(e->attr_len);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		if (index + sizeof(*e) + attr_size > len)
96862306a36Sopenharmony_ci			return;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		if (e->attr_type == IEEE80211_P2P_ATTR_CHANNEL_LIST &&
97162306a36Sopenharmony_ci		    attr_size >= (sizeof(struct wilc_attr_ch_list) - sizeof(*e)))
97262306a36Sopenharmony_ci			ch_list_idx = index;
97362306a36Sopenharmony_ci		else if (e->attr_type == IEEE80211_P2P_ATTR_OPER_CHANNEL &&
97462306a36Sopenharmony_ci			 attr_size == (sizeof(struct wilc_attr_oper_ch) - sizeof(*e)))
97562306a36Sopenharmony_ci			op_ch_idx = index;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci		if (ch_list_idx && op_ch_idx)
97862306a36Sopenharmony_ci			break;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		index += sizeof(*e) + attr_size;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (ch_list_idx) {
98462306a36Sopenharmony_ci		u16 elem_size;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		ch_list = (struct wilc_attr_ch_list *)&buf[ch_list_idx];
98762306a36Sopenharmony_ci		/* the number of bytes following the final 'elem' member */
98862306a36Sopenharmony_ci		elem_size = le16_to_cpu(ch_list->attr_len) -
98962306a36Sopenharmony_ci			(sizeof(*ch_list) - sizeof(struct wilc_attr_entry));
99062306a36Sopenharmony_ci		for (unsigned int i = 0; i < elem_size;) {
99162306a36Sopenharmony_ci			struct wilc_ch_list_elem *e;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci			e = (struct wilc_ch_list_elem *)(ch_list->elem + i);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci			i += sizeof(*e);
99662306a36Sopenharmony_ci			if (i > elem_size)
99762306a36Sopenharmony_ci				break;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci			i += e->no_of_channels;
100062306a36Sopenharmony_ci			if (i > elem_size)
100162306a36Sopenharmony_ci				break;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci			if (e->op_class == WILC_WLAN_OPERATING_CLASS_2_4GHZ) {
100462306a36Sopenharmony_ci				memset(e->ch_list, sta_ch, e->no_of_channels);
100562306a36Sopenharmony_ci				break;
100662306a36Sopenharmony_ci			}
100762306a36Sopenharmony_ci		}
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (op_ch_idx) {
101162306a36Sopenharmony_ci		op_ch = (struct wilc_attr_oper_ch *)&buf[op_ch_idx];
101262306a36Sopenharmony_ci		op_ch->op_class = WILC_WLAN_OPERATING_CLASS_2_4GHZ;
101362306a36Sopenharmony_ci		op_ch->op_channel = sta_ch;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cibool wilc_wfi_mgmt_frame_rx(struct wilc_vif *vif, u8 *buff, u32 size)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct wilc *wl = vif->wilc;
102062306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
102162306a36Sopenharmony_ci	int freq;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	return cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0);
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_civoid wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct wilc *wl = vif->wilc;
103162306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
103262306a36Sopenharmony_ci	struct host_if_drv *wfi_drv = priv->hif_drv;
103362306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt;
103462306a36Sopenharmony_ci	struct wilc_vendor_specific_ie *p;
103562306a36Sopenharmony_ci	struct wilc_p2p_pub_act_frame *d;
103662306a36Sopenharmony_ci	int ie_offset = offsetof(struct ieee80211_mgmt, u) + sizeof(*d);
103762306a36Sopenharmony_ci	const u8 *vendor_ie;
103862306a36Sopenharmony_ci	u32 header, pkt_offset;
103962306a36Sopenharmony_ci	s32 freq;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	header = get_unaligned_le32(buff - HOST_HDR_OFFSET);
104262306a36Sopenharmony_ci	pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
104562306a36Sopenharmony_ci		bool ack = false;
104662306a36Sopenharmony_ci		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)buff;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		if (ieee80211_is_probe_resp(hdr->frame_control) ||
104962306a36Sopenharmony_ci		    pkt_offset & IS_MGMT_STATUS_SUCCES)
105062306a36Sopenharmony_ci			ack = true;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		cfg80211_mgmt_tx_status(&priv->wdev, priv->tx_cookie, buff,
105362306a36Sopenharmony_ci					size, ack, GFP_KERNEL);
105462306a36Sopenharmony_ci		return;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	mgmt = (struct ieee80211_mgmt *)buff;
106062306a36Sopenharmony_ci	if (!ieee80211_is_action(mgmt->frame_control))
106162306a36Sopenharmony_ci		goto out_rx_mgmt;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (priv->cfg_scanning &&
106462306a36Sopenharmony_ci	    time_after_eq(jiffies, (unsigned long)wfi_drv->p2p_timeout)) {
106562306a36Sopenharmony_ci		netdev_dbg(vif->ndev, "Receiving action wrong ch\n");
106662306a36Sopenharmony_ci		return;
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (!ieee80211_is_public_action((struct ieee80211_hdr *)buff, size))
107062306a36Sopenharmony_ci		goto out_rx_mgmt;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
107362306a36Sopenharmony_ci	if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP &&
107462306a36Sopenharmony_ci	    d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP)
107562306a36Sopenharmony_ci		goto out_rx_mgmt;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
107862306a36Sopenharmony_ci					    buff + ie_offset, size - ie_offset);
107962306a36Sopenharmony_ci	if (!vendor_ie)
108062306a36Sopenharmony_ci		goto out_rx_mgmt;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	p = (struct wilc_vendor_specific_ie *)vendor_ie;
108362306a36Sopenharmony_ci	wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ciout_rx_mgmt:
108662306a36Sopenharmony_ci	cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0);
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic void wilc_wfi_mgmt_tx_complete(void *priv, int status)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	struct wilc_p2p_mgmt_data *pv_data = priv;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	kfree(pv_data->buff);
109462306a36Sopenharmony_ci	kfree(pv_data);
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic void wilc_wfi_remain_on_channel_expired(void *data, u64 cookie)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct wilc_vif *vif = data;
110062306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
110162306a36Sopenharmony_ci	struct wilc_wfi_p2p_listen_params *params = &priv->remain_on_ch_params;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	if (cookie != params->listen_cookie)
110462306a36Sopenharmony_ci		return;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	priv->p2p_listen_state = false;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	cfg80211_remain_on_channel_expired(&priv->wdev, params->listen_cookie,
110962306a36Sopenharmony_ci					   params->listen_ch, GFP_KERNEL);
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic int remain_on_channel(struct wiphy *wiphy,
111362306a36Sopenharmony_ci			     struct wireless_dev *wdev,
111462306a36Sopenharmony_ci			     struct ieee80211_channel *chan,
111562306a36Sopenharmony_ci			     unsigned int duration, u64 *cookie)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	int ret = 0;
111862306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(wdev->netdev);
111962306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
112062306a36Sopenharmony_ci	u64 id;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	if (wdev->iftype == NL80211_IFTYPE_AP) {
112362306a36Sopenharmony_ci		netdev_dbg(vif->ndev, "Required while in AP mode\n");
112462306a36Sopenharmony_ci		return ret;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	id = ++priv->inc_roc_cookie;
112862306a36Sopenharmony_ci	if (id == 0)
112962306a36Sopenharmony_ci		id = ++priv->inc_roc_cookie;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	ret = wilc_remain_on_channel(vif, id, duration, chan->hw_value,
113262306a36Sopenharmony_ci				     wilc_wfi_remain_on_channel_expired,
113362306a36Sopenharmony_ci				     (void *)vif);
113462306a36Sopenharmony_ci	if (ret)
113562306a36Sopenharmony_ci		return ret;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	vif->wilc->op_ch = chan->hw_value;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	priv->remain_on_ch_params.listen_ch = chan;
114062306a36Sopenharmony_ci	priv->remain_on_ch_params.listen_cookie = id;
114162306a36Sopenharmony_ci	*cookie = id;
114262306a36Sopenharmony_ci	priv->p2p_listen_state = true;
114362306a36Sopenharmony_ci	priv->remain_on_ch_params.listen_duration = duration;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL);
114662306a36Sopenharmony_ci	mod_timer(&vif->hif_drv->remain_on_ch_timer,
114762306a36Sopenharmony_ci		  jiffies + msecs_to_jiffies(duration + 1000));
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	return ret;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic int cancel_remain_on_channel(struct wiphy *wiphy,
115362306a36Sopenharmony_ci				    struct wireless_dev *wdev,
115462306a36Sopenharmony_ci				    u64 cookie)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(wdev->netdev);
115762306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (cookie != priv->remain_on_ch_params.listen_cookie)
116062306a36Sopenharmony_ci		return -ENOENT;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	return wilc_listen_state_expired(vif, cookie);
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic int mgmt_tx(struct wiphy *wiphy,
116662306a36Sopenharmony_ci		   struct wireless_dev *wdev,
116762306a36Sopenharmony_ci		   struct cfg80211_mgmt_tx_params *params,
116862306a36Sopenharmony_ci		   u64 *cookie)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct ieee80211_channel *chan = params->chan;
117162306a36Sopenharmony_ci	unsigned int wait = params->wait;
117262306a36Sopenharmony_ci	const u8 *buf = params->buf;
117362306a36Sopenharmony_ci	size_t len = params->len;
117462306a36Sopenharmony_ci	const struct ieee80211_mgmt *mgmt;
117562306a36Sopenharmony_ci	struct wilc_p2p_mgmt_data *mgmt_tx;
117662306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(wdev->netdev);
117762306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
117862306a36Sopenharmony_ci	struct host_if_drv *wfi_drv = priv->hif_drv;
117962306a36Sopenharmony_ci	struct wilc_vendor_specific_ie *p;
118062306a36Sopenharmony_ci	struct wilc_p2p_pub_act_frame *d;
118162306a36Sopenharmony_ci	int ie_offset = offsetof(struct ieee80211_mgmt, u) + sizeof(*d);
118262306a36Sopenharmony_ci	const u8 *vendor_ie;
118362306a36Sopenharmony_ci	int ret = 0;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	*cookie = get_random_u32();
118662306a36Sopenharmony_ci	priv->tx_cookie = *cookie;
118762306a36Sopenharmony_ci	mgmt = (const struct ieee80211_mgmt *)buf;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (!ieee80211_is_mgmt(mgmt->frame_control))
119062306a36Sopenharmony_ci		goto out;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_KERNEL);
119362306a36Sopenharmony_ci	if (!mgmt_tx) {
119462306a36Sopenharmony_ci		ret = -ENOMEM;
119562306a36Sopenharmony_ci		goto out;
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	mgmt_tx->buff = kmemdup(buf, len, GFP_KERNEL);
119962306a36Sopenharmony_ci	if (!mgmt_tx->buff) {
120062306a36Sopenharmony_ci		ret = -ENOMEM;
120162306a36Sopenharmony_ci		kfree(mgmt_tx);
120262306a36Sopenharmony_ci		goto out;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	mgmt_tx->size = len;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
120862306a36Sopenharmony_ci		wilc_set_mac_chnl_num(vif, chan->hw_value);
120962306a36Sopenharmony_ci		vif->wilc->op_ch = chan->hw_value;
121062306a36Sopenharmony_ci		goto out_txq_add_pkt;
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if (!ieee80211_is_public_action((struct ieee80211_hdr *)buf, len)) {
121462306a36Sopenharmony_ci		if (chan)
121562306a36Sopenharmony_ci			wilc_set_mac_chnl_num(vif, chan->hw_value);
121662306a36Sopenharmony_ci		else
121762306a36Sopenharmony_ci			wilc_set_mac_chnl_num(vif, vif->wilc->op_ch);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		goto out_set_timeout;
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
122362306a36Sopenharmony_ci	if (d->oui_type != WLAN_OUI_TYPE_WFA_P2P ||
122462306a36Sopenharmony_ci	    d->oui_subtype != GO_NEG_CONF) {
122562306a36Sopenharmony_ci		wilc_set_mac_chnl_num(vif, chan->hw_value);
122662306a36Sopenharmony_ci		vif->wilc->op_ch = chan->hw_value;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP)
123062306a36Sopenharmony_ci		goto out_set_timeout;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
123362306a36Sopenharmony_ci					    mgmt_tx->buff + ie_offset,
123462306a36Sopenharmony_ci					    len - ie_offset);
123562306a36Sopenharmony_ci	if (!vendor_ie)
123662306a36Sopenharmony_ci		goto out_set_timeout;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	p = (struct wilc_vendor_specific_ie *)vendor_ie;
123962306a36Sopenharmony_ci	wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ciout_set_timeout:
124262306a36Sopenharmony_ci	wfi_drv->p2p_timeout = (jiffies + msecs_to_jiffies(wait));
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ciout_txq_add_pkt:
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	wilc_wlan_txq_add_mgmt_pkt(wdev->netdev, mgmt_tx,
124762306a36Sopenharmony_ci				   mgmt_tx->buff, mgmt_tx->size,
124862306a36Sopenharmony_ci				   wilc_wfi_mgmt_tx_complete);
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ciout:
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	return ret;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cistatic int mgmt_tx_cancel_wait(struct wiphy *wiphy,
125662306a36Sopenharmony_ci			       struct wireless_dev *wdev,
125762306a36Sopenharmony_ci			       u64 cookie)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(wdev->netdev);
126062306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
126162306a36Sopenharmony_ci	struct host_if_drv *wfi_drv = priv->hif_drv;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	wfi_drv->p2p_timeout = jiffies;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (!priv->p2p_listen_state) {
126662306a36Sopenharmony_ci		struct wilc_wfi_p2p_listen_params *params;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci		params = &priv->remain_on_ch_params;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		cfg80211_remain_on_channel_expired(wdev,
127162306a36Sopenharmony_ci						   params->listen_cookie,
127262306a36Sopenharmony_ci						   params->listen_ch,
127362306a36Sopenharmony_ci						   GFP_KERNEL);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	return 0;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_civoid wilc_update_mgmt_frame_registrations(struct wiphy *wiphy,
128062306a36Sopenharmony_ci					  struct wireless_dev *wdev,
128162306a36Sopenharmony_ci					  struct mgmt_frame_regs *upd)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
128462306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(wdev->netdev);
128562306a36Sopenharmony_ci	u32 presp_bit = BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
128662306a36Sopenharmony_ci	u32 action_bit = BIT(IEEE80211_STYPE_ACTION >> 4);
128762306a36Sopenharmony_ci	u32 pauth_bit = BIT(IEEE80211_STYPE_AUTH >> 4);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (wl->initialized) {
129062306a36Sopenharmony_ci		bool prev = vif->mgmt_reg_stypes & presp_bit;
129162306a36Sopenharmony_ci		bool now = upd->interface_stypes & presp_bit;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		if (now != prev)
129462306a36Sopenharmony_ci			wilc_frame_register(vif, IEEE80211_STYPE_PROBE_REQ, now);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		prev = vif->mgmt_reg_stypes & action_bit;
129762306a36Sopenharmony_ci		now = upd->interface_stypes & action_bit;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci		if (now != prev)
130062306a36Sopenharmony_ci			wilc_frame_register(vif, IEEE80211_STYPE_ACTION, now);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		prev = vif->mgmt_reg_stypes & pauth_bit;
130362306a36Sopenharmony_ci		now = upd->interface_stypes & pauth_bit;
130462306a36Sopenharmony_ci		if (now != prev)
130562306a36Sopenharmony_ci			wilc_frame_register(vif, IEEE80211_STYPE_AUTH, now);
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	vif->mgmt_reg_stypes =
130962306a36Sopenharmony_ci		upd->interface_stypes & (presp_bit | action_bit | pauth_bit);
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_cistatic int external_auth(struct wiphy *wiphy, struct net_device *dev,
131362306a36Sopenharmony_ci			 struct cfg80211_external_auth_params *auth)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (auth->status == WLAN_STATUS_SUCCESS)
131862306a36Sopenharmony_ci		wilc_set_external_auth_param(vif, auth);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic int set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev,
132462306a36Sopenharmony_ci			       s32 rssi_thold, u32 rssi_hyst)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	return 0;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic int dump_station(struct wiphy *wiphy, struct net_device *dev,
133062306a36Sopenharmony_ci			int idx, u8 *mac, struct station_info *sinfo)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
133362306a36Sopenharmony_ci	int ret;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if (idx != 0)
133662306a36Sopenharmony_ci		return -ENOENT;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	ret = wilc_get_rssi(vif, &sinfo->signal);
133962306a36Sopenharmony_ci	if (ret)
134062306a36Sopenharmony_ci		return ret;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
134362306a36Sopenharmony_ci	memcpy(mac, vif->priv.associated_bss, ETH_ALEN);
134462306a36Sopenharmony_ci	return 0;
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
134862306a36Sopenharmony_ci			  bool enabled, int timeout)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
135162306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (!priv->hif_drv)
135462306a36Sopenharmony_ci		return -EIO;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	wilc_set_power_mgmt(vif, enabled, timeout);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	return 0;
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
136262306a36Sopenharmony_ci			       enum nl80211_iftype type,
136362306a36Sopenharmony_ci			       struct vif_params *params)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
136662306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
136762306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	switch (type) {
137062306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
137162306a36Sopenharmony_ci		vif->connecting = false;
137262306a36Sopenharmony_ci		dev->ieee80211_ptr->iftype = type;
137362306a36Sopenharmony_ci		priv->wdev.iftype = type;
137462306a36Sopenharmony_ci		vif->monitor_flag = 0;
137562306a36Sopenharmony_ci		if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE)
137662306a36Sopenharmony_ci			wilc_wfi_deinit_mon_interface(wl, true);
137762306a36Sopenharmony_ci		vif->iftype = WILC_STATION_MODE;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci		if (wl->initialized)
138062306a36Sopenharmony_ci			wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
138162306a36Sopenharmony_ci						WILC_STATION_MODE, vif->idx);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci		memset(priv->assoc_stainfo.sta_associated_bss, 0,
138462306a36Sopenharmony_ci		       WILC_MAX_NUM_STA * ETH_ALEN);
138562306a36Sopenharmony_ci		break;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
138862306a36Sopenharmony_ci		vif->connecting = false;
138962306a36Sopenharmony_ci		dev->ieee80211_ptr->iftype = type;
139062306a36Sopenharmony_ci		priv->wdev.iftype = type;
139162306a36Sopenharmony_ci		vif->monitor_flag = 0;
139262306a36Sopenharmony_ci		vif->iftype = WILC_CLIENT_MODE;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci		if (wl->initialized)
139562306a36Sopenharmony_ci			wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
139662306a36Sopenharmony_ci						WILC_STATION_MODE, vif->idx);
139762306a36Sopenharmony_ci		break;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
140062306a36Sopenharmony_ci		dev->ieee80211_ptr->iftype = type;
140162306a36Sopenharmony_ci		priv->wdev.iftype = type;
140262306a36Sopenharmony_ci		vif->iftype = WILC_AP_MODE;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		if (wl->initialized)
140562306a36Sopenharmony_ci			wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
140662306a36Sopenharmony_ci						WILC_AP_MODE, vif->idx);
140762306a36Sopenharmony_ci		break;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
141062306a36Sopenharmony_ci		dev->ieee80211_ptr->iftype = type;
141162306a36Sopenharmony_ci		priv->wdev.iftype = type;
141262306a36Sopenharmony_ci		vif->iftype = WILC_GO_MODE;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci		if (wl->initialized)
141562306a36Sopenharmony_ci			wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
141662306a36Sopenharmony_ci						WILC_AP_MODE, vif->idx);
141762306a36Sopenharmony_ci		break;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	default:
142062306a36Sopenharmony_ci		netdev_err(dev, "Unknown interface type= %d\n", type);
142162306a36Sopenharmony_ci		return -EINVAL;
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	return 0;
142562306a36Sopenharmony_ci}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic int start_ap(struct wiphy *wiphy, struct net_device *dev,
142862306a36Sopenharmony_ci		    struct cfg80211_ap_settings *settings)
142962306a36Sopenharmony_ci{
143062306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
143162306a36Sopenharmony_ci	int ret;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	ret = set_channel(wiphy, &settings->chandef);
143462306a36Sopenharmony_ci	if (ret != 0)
143562306a36Sopenharmony_ci		netdev_err(dev, "Error in setting channel\n");
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	wilc_wlan_set_bssid(dev, dev->dev_addr, WILC_AP_MODE);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	return wilc_add_beacon(vif, settings->beacon_interval,
144062306a36Sopenharmony_ci				   settings->dtim_period, &settings->beacon);
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic int change_beacon(struct wiphy *wiphy, struct net_device *dev,
144462306a36Sopenharmony_ci			 struct cfg80211_beacon_data *beacon)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	return wilc_add_beacon(vif, 0, 0, beacon);
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_cistatic int stop_ap(struct wiphy *wiphy, struct net_device *dev,
145262306a36Sopenharmony_ci		   unsigned int link_id)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	int ret;
145562306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	wilc_wlan_set_bssid(dev, NULL, WILC_AP_MODE);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	ret = wilc_del_beacon(vif);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	if (ret)
146262306a36Sopenharmony_ci		netdev_err(dev, "Host delete beacon fail\n");
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	return ret;
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_cistatic int add_station(struct wiphy *wiphy, struct net_device *dev,
146862306a36Sopenharmony_ci		       const u8 *mac, struct station_parameters *params)
146962306a36Sopenharmony_ci{
147062306a36Sopenharmony_ci	int ret = 0;
147162306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
147262306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
147562306a36Sopenharmony_ci		memcpy(priv->assoc_stainfo.sta_associated_bss[params->aid], mac,
147662306a36Sopenharmony_ci		       ETH_ALEN);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci		ret = wilc_add_station(vif, mac, params);
147962306a36Sopenharmony_ci		if (ret)
148062306a36Sopenharmony_ci			netdev_err(dev, "Host add station fail\n");
148162306a36Sopenharmony_ci	}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	return ret;
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic int del_station(struct wiphy *wiphy, struct net_device *dev,
148762306a36Sopenharmony_ci		       struct station_del_parameters *params)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	const u8 *mac = params->mac;
149062306a36Sopenharmony_ci	int ret = 0;
149162306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
149262306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
149362306a36Sopenharmony_ci	struct sta_info *info;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	if (!(vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE))
149662306a36Sopenharmony_ci		return ret;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	info = &priv->assoc_stainfo;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (!mac)
150162306a36Sopenharmony_ci		ret = wilc_del_allstation(vif, info->sta_associated_bss);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	ret = wilc_del_station(vif, mac);
150462306a36Sopenharmony_ci	if (ret)
150562306a36Sopenharmony_ci		netdev_err(dev, "Host delete station fail\n");
150662306a36Sopenharmony_ci	return ret;
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_cistatic int change_station(struct wiphy *wiphy, struct net_device *dev,
151062306a36Sopenharmony_ci			  const u8 *mac, struct station_parameters *params)
151162306a36Sopenharmony_ci{
151262306a36Sopenharmony_ci	int ret = 0;
151362306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(dev);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) {
151662306a36Sopenharmony_ci		ret = wilc_edit_station(vif, mac, params);
151762306a36Sopenharmony_ci		if (ret)
151862306a36Sopenharmony_ci			netdev_err(dev, "Host edit station fail\n");
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci	return ret;
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_cistatic struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type)
152462306a36Sopenharmony_ci{
152562306a36Sopenharmony_ci	struct wilc_vif *vif;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	list_for_each_entry_rcu(vif, &wl->vif_list, list) {
152862306a36Sopenharmony_ci		if (vif->iftype == type)
152962306a36Sopenharmony_ci			return vif;
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	return NULL;
153362306a36Sopenharmony_ci}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_cistatic struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
153662306a36Sopenharmony_ci					     const char *name,
153762306a36Sopenharmony_ci					     unsigned char name_assign_type,
153862306a36Sopenharmony_ci					     enum nl80211_iftype type,
153962306a36Sopenharmony_ci					     struct vif_params *params)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
154262306a36Sopenharmony_ci	struct wilc_vif *vif;
154362306a36Sopenharmony_ci	struct wireless_dev *wdev;
154462306a36Sopenharmony_ci	int iftype;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	if (type == NL80211_IFTYPE_MONITOR) {
154762306a36Sopenharmony_ci		struct net_device *ndev;
154862306a36Sopenharmony_ci		int srcu_idx;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		srcu_idx = srcu_read_lock(&wl->srcu);
155162306a36Sopenharmony_ci		vif = wilc_get_vif_from_type(wl, WILC_AP_MODE);
155262306a36Sopenharmony_ci		if (!vif) {
155362306a36Sopenharmony_ci			vif = wilc_get_vif_from_type(wl, WILC_GO_MODE);
155462306a36Sopenharmony_ci			if (!vif) {
155562306a36Sopenharmony_ci				srcu_read_unlock(&wl->srcu, srcu_idx);
155662306a36Sopenharmony_ci				goto validate_interface;
155762306a36Sopenharmony_ci			}
155862306a36Sopenharmony_ci		}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci		if (vif->monitor_flag) {
156162306a36Sopenharmony_ci			srcu_read_unlock(&wl->srcu, srcu_idx);
156262306a36Sopenharmony_ci			goto validate_interface;
156362306a36Sopenharmony_ci		}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci		ndev = wilc_wfi_init_mon_interface(wl, name, vif->ndev);
156662306a36Sopenharmony_ci		if (ndev) {
156762306a36Sopenharmony_ci			vif->monitor_flag = 1;
156862306a36Sopenharmony_ci		} else {
156962306a36Sopenharmony_ci			srcu_read_unlock(&wl->srcu, srcu_idx);
157062306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
157162306a36Sopenharmony_ci		}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci		wdev = &vif->priv.wdev;
157462306a36Sopenharmony_ci		srcu_read_unlock(&wl->srcu, srcu_idx);
157562306a36Sopenharmony_ci		return wdev;
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_civalidate_interface:
157962306a36Sopenharmony_ci	mutex_lock(&wl->vif_mutex);
158062306a36Sopenharmony_ci	if (wl->vif_num == WILC_NUM_CONCURRENT_IFC) {
158162306a36Sopenharmony_ci		pr_err("Reached maximum number of interface\n");
158262306a36Sopenharmony_ci		mutex_unlock(&wl->vif_mutex);
158362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci	mutex_unlock(&wl->vif_mutex);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	switch (type) {
158862306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
158962306a36Sopenharmony_ci		iftype = WILC_STATION_MODE;
159062306a36Sopenharmony_ci		break;
159162306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
159262306a36Sopenharmony_ci		iftype = WILC_AP_MODE;
159362306a36Sopenharmony_ci		break;
159462306a36Sopenharmony_ci	default:
159562306a36Sopenharmony_ci		return ERR_PTR(-EOPNOTSUPP);
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	vif = wilc_netdev_ifc_init(wl, name, iftype, type, true);
159962306a36Sopenharmony_ci	if (IS_ERR(vif))
160062306a36Sopenharmony_ci		return ERR_CAST(vif);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return &vif->priv.wdev;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
160862306a36Sopenharmony_ci	struct wilc_vif *vif;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	if (wdev->iftype == NL80211_IFTYPE_AP ||
161162306a36Sopenharmony_ci	    wdev->iftype == NL80211_IFTYPE_P2P_GO)
161262306a36Sopenharmony_ci		wilc_wfi_deinit_mon_interface(wl, true);
161362306a36Sopenharmony_ci	vif = netdev_priv(wdev->netdev);
161462306a36Sopenharmony_ci	cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL);
161562306a36Sopenharmony_ci	cfg80211_unregister_netdevice(vif->ndev);
161662306a36Sopenharmony_ci	vif->monitor_flag = 0;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	mutex_lock(&wl->vif_mutex);
161962306a36Sopenharmony_ci	list_del_rcu(&vif->list);
162062306a36Sopenharmony_ci	wl->vif_num--;
162162306a36Sopenharmony_ci	mutex_unlock(&wl->vif_mutex);
162262306a36Sopenharmony_ci	synchronize_srcu(&wl->srcu);
162362306a36Sopenharmony_ci	return 0;
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_cistatic int wilc_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	if (!wow && wilc_wlan_get_num_conn_ifcs(wl))
163162306a36Sopenharmony_ci		wl->suspend_event = true;
163262306a36Sopenharmony_ci	else
163362306a36Sopenharmony_ci		wl->suspend_event = false;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	return 0;
163662306a36Sopenharmony_ci}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cistatic int wilc_resume(struct wiphy *wiphy)
163962306a36Sopenharmony_ci{
164062306a36Sopenharmony_ci	return 0;
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic void wilc_set_wakeup(struct wiphy *wiphy, bool enabled)
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
164662306a36Sopenharmony_ci	struct wilc_vif *vif;
164762306a36Sopenharmony_ci	int srcu_idx;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	srcu_idx = srcu_read_lock(&wl->srcu);
165062306a36Sopenharmony_ci	vif = wilc_get_wl_to_vif(wl);
165162306a36Sopenharmony_ci	if (IS_ERR(vif)) {
165262306a36Sopenharmony_ci		srcu_read_unlock(&wl->srcu, srcu_idx);
165362306a36Sopenharmony_ci		return;
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	netdev_info(vif->ndev, "cfg set wake up = %d\n", enabled);
165762306a36Sopenharmony_ci	wilc_set_wowlan_trigger(vif, enabled);
165862306a36Sopenharmony_ci	srcu_read_unlock(&wl->srcu, srcu_idx);
165962306a36Sopenharmony_ci}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_cistatic int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
166262306a36Sopenharmony_ci			enum nl80211_tx_power_setting type, int mbm)
166362306a36Sopenharmony_ci{
166462306a36Sopenharmony_ci	int ret;
166562306a36Sopenharmony_ci	int srcu_idx;
166662306a36Sopenharmony_ci	s32 tx_power = MBM_TO_DBM(mbm);
166762306a36Sopenharmony_ci	struct wilc *wl = wiphy_priv(wiphy);
166862306a36Sopenharmony_ci	struct wilc_vif *vif;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	if (!wl->initialized)
167162306a36Sopenharmony_ci		return -EIO;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	srcu_idx = srcu_read_lock(&wl->srcu);
167462306a36Sopenharmony_ci	vif = wilc_get_wl_to_vif(wl);
167562306a36Sopenharmony_ci	if (IS_ERR(vif)) {
167662306a36Sopenharmony_ci		srcu_read_unlock(&wl->srcu, srcu_idx);
167762306a36Sopenharmony_ci		return -EINVAL;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	netdev_info(vif->ndev, "Setting tx power %d\n", tx_power);
168162306a36Sopenharmony_ci	if (tx_power < 0)
168262306a36Sopenharmony_ci		tx_power = 0;
168362306a36Sopenharmony_ci	else if (tx_power > 18)
168462306a36Sopenharmony_ci		tx_power = 18;
168562306a36Sopenharmony_ci	ret = wilc_set_tx_power(vif, tx_power);
168662306a36Sopenharmony_ci	if (ret)
168762306a36Sopenharmony_ci		netdev_err(vif->ndev, "Failed to set tx power\n");
168862306a36Sopenharmony_ci	srcu_read_unlock(&wl->srcu, srcu_idx);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	return ret;
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic int get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
169462306a36Sopenharmony_ci			int *dbm)
169562306a36Sopenharmony_ci{
169662306a36Sopenharmony_ci	int ret;
169762306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(wdev->netdev);
169862306a36Sopenharmony_ci	struct wilc *wl = vif->wilc;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	/* If firmware is not started, return. */
170162306a36Sopenharmony_ci	if (!wl->initialized)
170262306a36Sopenharmony_ci		return -EIO;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	ret = wilc_get_tx_power(vif, (u8 *)dbm);
170562306a36Sopenharmony_ci	if (ret)
170662306a36Sopenharmony_ci		netdev_err(vif->ndev, "Failed to get tx power\n");
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	return ret;
170962306a36Sopenharmony_ci}
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_cistatic const struct cfg80211_ops wilc_cfg80211_ops = {
171262306a36Sopenharmony_ci	.set_monitor_channel = set_channel,
171362306a36Sopenharmony_ci	.scan = scan,
171462306a36Sopenharmony_ci	.connect = connect,
171562306a36Sopenharmony_ci	.disconnect = disconnect,
171662306a36Sopenharmony_ci	.add_key = add_key,
171762306a36Sopenharmony_ci	.del_key = del_key,
171862306a36Sopenharmony_ci	.get_key = get_key,
171962306a36Sopenharmony_ci	.set_default_key = set_default_key,
172062306a36Sopenharmony_ci	.set_default_mgmt_key = set_default_mgmt_key,
172162306a36Sopenharmony_ci	.add_virtual_intf = add_virtual_intf,
172262306a36Sopenharmony_ci	.del_virtual_intf = del_virtual_intf,
172362306a36Sopenharmony_ci	.change_virtual_intf = change_virtual_intf,
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	.start_ap = start_ap,
172662306a36Sopenharmony_ci	.change_beacon = change_beacon,
172762306a36Sopenharmony_ci	.stop_ap = stop_ap,
172862306a36Sopenharmony_ci	.add_station = add_station,
172962306a36Sopenharmony_ci	.del_station = del_station,
173062306a36Sopenharmony_ci	.change_station = change_station,
173162306a36Sopenharmony_ci	.get_station = get_station,
173262306a36Sopenharmony_ci	.dump_station = dump_station,
173362306a36Sopenharmony_ci	.change_bss = change_bss,
173462306a36Sopenharmony_ci	.set_wiphy_params = set_wiphy_params,
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	.external_auth = external_auth,
173762306a36Sopenharmony_ci	.set_pmksa = set_pmksa,
173862306a36Sopenharmony_ci	.del_pmksa = del_pmksa,
173962306a36Sopenharmony_ci	.flush_pmksa = flush_pmksa,
174062306a36Sopenharmony_ci	.remain_on_channel = remain_on_channel,
174162306a36Sopenharmony_ci	.cancel_remain_on_channel = cancel_remain_on_channel,
174262306a36Sopenharmony_ci	.mgmt_tx_cancel_wait = mgmt_tx_cancel_wait,
174362306a36Sopenharmony_ci	.mgmt_tx = mgmt_tx,
174462306a36Sopenharmony_ci	.update_mgmt_frame_registrations = wilc_update_mgmt_frame_registrations,
174562306a36Sopenharmony_ci	.set_power_mgmt = set_power_mgmt,
174662306a36Sopenharmony_ci	.set_cqm_rssi_config = set_cqm_rssi_config,
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	.suspend = wilc_suspend,
174962306a36Sopenharmony_ci	.resume = wilc_resume,
175062306a36Sopenharmony_ci	.set_wakeup = wilc_set_wakeup,
175162306a36Sopenharmony_ci	.set_tx_power = set_tx_power,
175262306a36Sopenharmony_ci	.get_tx_power = get_tx_power,
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci};
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_cistatic void wlan_init_locks(struct wilc *wl)
175762306a36Sopenharmony_ci{
175862306a36Sopenharmony_ci	mutex_init(&wl->hif_cs);
175962306a36Sopenharmony_ci	mutex_init(&wl->rxq_cs);
176062306a36Sopenharmony_ci	mutex_init(&wl->cfg_cmd_lock);
176162306a36Sopenharmony_ci	mutex_init(&wl->vif_mutex);
176262306a36Sopenharmony_ci	mutex_init(&wl->deinit_lock);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	spin_lock_init(&wl->txq_spinlock);
176562306a36Sopenharmony_ci	mutex_init(&wl->txq_add_to_head_cs);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	init_completion(&wl->txq_event);
176862306a36Sopenharmony_ci	init_completion(&wl->cfg_event);
176962306a36Sopenharmony_ci	init_completion(&wl->sync_event);
177062306a36Sopenharmony_ci	init_completion(&wl->txq_thread_started);
177162306a36Sopenharmony_ci	init_srcu_struct(&wl->srcu);
177262306a36Sopenharmony_ci}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_civoid wlan_deinit_locks(struct wilc *wilc)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	mutex_destroy(&wilc->hif_cs);
177762306a36Sopenharmony_ci	mutex_destroy(&wilc->rxq_cs);
177862306a36Sopenharmony_ci	mutex_destroy(&wilc->cfg_cmd_lock);
177962306a36Sopenharmony_ci	mutex_destroy(&wilc->txq_add_to_head_cs);
178062306a36Sopenharmony_ci	mutex_destroy(&wilc->vif_mutex);
178162306a36Sopenharmony_ci	mutex_destroy(&wilc->deinit_lock);
178262306a36Sopenharmony_ci	cleanup_srcu_struct(&wilc->srcu);
178362306a36Sopenharmony_ci}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ciint wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type,
178662306a36Sopenharmony_ci		       const struct wilc_hif_func *ops)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	struct wilc *wl;
178962306a36Sopenharmony_ci	struct wilc_vif *vif;
179062306a36Sopenharmony_ci	int ret, i;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	wl = wilc_create_wiphy(dev);
179362306a36Sopenharmony_ci	if (!wl)
179462306a36Sopenharmony_ci		return -EINVAL;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	wlan_init_locks(wl);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	ret = wilc_wlan_cfg_init(wl);
179962306a36Sopenharmony_ci	if (ret)
180062306a36Sopenharmony_ci		goto free_wl;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	*wilc = wl;
180362306a36Sopenharmony_ci	wl->io_type = io_type;
180462306a36Sopenharmony_ci	wl->hif_func = ops;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	for (i = 0; i < NQUEUES; i++)
180762306a36Sopenharmony_ci		INIT_LIST_HEAD(&wl->txq[i].txq_head.list);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	INIT_LIST_HEAD(&wl->rxq_head.list);
181062306a36Sopenharmony_ci	INIT_LIST_HEAD(&wl->vif_list);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	wl->hif_workqueue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM,
181362306a36Sopenharmony_ci						    wiphy_name(wl->wiphy));
181462306a36Sopenharmony_ci	if (!wl->hif_workqueue) {
181562306a36Sopenharmony_ci		ret = -ENOMEM;
181662306a36Sopenharmony_ci		goto free_cfg;
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci	vif = wilc_netdev_ifc_init(wl, "wlan%d", WILC_STATION_MODE,
181962306a36Sopenharmony_ci				   NL80211_IFTYPE_STATION, false);
182062306a36Sopenharmony_ci	if (IS_ERR(vif)) {
182162306a36Sopenharmony_ci		ret = PTR_ERR(vif);
182262306a36Sopenharmony_ci		goto free_hq;
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	return 0;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_cifree_hq:
182862306a36Sopenharmony_ci	destroy_workqueue(wl->hif_workqueue);
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_cifree_cfg:
183162306a36Sopenharmony_ci	wilc_wlan_cfg_deinit(wl);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_cifree_wl:
183462306a36Sopenharmony_ci	wlan_deinit_locks(wl);
183562306a36Sopenharmony_ci	wiphy_unregister(wl->wiphy);
183662306a36Sopenharmony_ci	wiphy_free(wl->wiphy);
183762306a36Sopenharmony_ci	return ret;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wilc_cfg80211_init);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_cistruct wilc *wilc_create_wiphy(struct device *dev)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	struct wiphy *wiphy;
184462306a36Sopenharmony_ci	struct wilc *wl;
184562306a36Sopenharmony_ci	int ret;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	wiphy = wiphy_new(&wilc_cfg80211_ops, sizeof(*wl));
184862306a36Sopenharmony_ci	if (!wiphy)
184962306a36Sopenharmony_ci		return NULL;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	wl = wiphy_priv(wiphy);
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	memcpy(wl->bitrates, wilc_bitrates, sizeof(wilc_bitrates));
185462306a36Sopenharmony_ci	memcpy(wl->channels, wilc_2ghz_channels, sizeof(wilc_2ghz_channels));
185562306a36Sopenharmony_ci	wl->band.bitrates = wl->bitrates;
185662306a36Sopenharmony_ci	wl->band.n_bitrates = ARRAY_SIZE(wl->bitrates);
185762306a36Sopenharmony_ci	wl->band.channels = wl->channels;
185862306a36Sopenharmony_ci	wl->band.n_channels = ARRAY_SIZE(wilc_2ghz_channels);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	wl->band.ht_cap.ht_supported = 1;
186162306a36Sopenharmony_ci	wl->band.ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
186262306a36Sopenharmony_ci	wl->band.ht_cap.mcs.rx_mask[0] = 0xff;
186362306a36Sopenharmony_ci	wl->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
186462306a36Sopenharmony_ci	wl->band.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	wiphy->bands[NL80211_BAND_2GHZ] = &wl->band;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	wiphy->max_scan_ssids = WILC_MAX_NUM_PROBED_SSID;
186962306a36Sopenharmony_ci#ifdef CONFIG_PM
187062306a36Sopenharmony_ci	wiphy->wowlan = &wowlan_support;
187162306a36Sopenharmony_ci#endif
187262306a36Sopenharmony_ci	wiphy->max_num_pmkids = WILC_MAX_NUM_PMKIDS;
187362306a36Sopenharmony_ci	wiphy->max_scan_ie_len = 1000;
187462306a36Sopenharmony_ci	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
187562306a36Sopenharmony_ci	memcpy(wl->cipher_suites, wilc_cipher_suites,
187662306a36Sopenharmony_ci	       sizeof(wilc_cipher_suites));
187762306a36Sopenharmony_ci	wiphy->cipher_suites = wl->cipher_suites;
187862306a36Sopenharmony_ci	wiphy->n_cipher_suites = ARRAY_SIZE(wilc_cipher_suites);
187962306a36Sopenharmony_ci	wiphy->mgmt_stypes = wilc_wfi_cfg80211_mgmt_types;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	wiphy->max_remain_on_channel_duration = 500;
188262306a36Sopenharmony_ci	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
188362306a36Sopenharmony_ci				BIT(NL80211_IFTYPE_AP) |
188462306a36Sopenharmony_ci				BIT(NL80211_IFTYPE_MONITOR) |
188562306a36Sopenharmony_ci				BIT(NL80211_IFTYPE_P2P_GO) |
188662306a36Sopenharmony_ci				BIT(NL80211_IFTYPE_P2P_CLIENT);
188762306a36Sopenharmony_ci	wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
188862306a36Sopenharmony_ci	wiphy->features |= NL80211_FEATURE_SAE;
188962306a36Sopenharmony_ci	set_wiphy_dev(wiphy, dev);
189062306a36Sopenharmony_ci	wl->wiphy = wiphy;
189162306a36Sopenharmony_ci	ret = wiphy_register(wiphy);
189262306a36Sopenharmony_ci	if (ret) {
189362306a36Sopenharmony_ci		wiphy_free(wiphy);
189462306a36Sopenharmony_ci		return NULL;
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci	return wl;
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ciint wilc_init_host_int(struct net_device *net)
190062306a36Sopenharmony_ci{
190162306a36Sopenharmony_ci	int ret;
190262306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(net);
190362306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	priv->p2p_listen_state = false;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	mutex_init(&priv->scan_req_lock);
190862306a36Sopenharmony_ci	ret = wilc_init(net, &priv->hif_drv);
190962306a36Sopenharmony_ci	if (ret)
191062306a36Sopenharmony_ci		netdev_err(net, "Error while initializing hostinterface\n");
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	return ret;
191362306a36Sopenharmony_ci}
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_civoid wilc_deinit_host_int(struct net_device *net)
191662306a36Sopenharmony_ci{
191762306a36Sopenharmony_ci	int ret;
191862306a36Sopenharmony_ci	struct wilc_vif *vif = netdev_priv(net);
191962306a36Sopenharmony_ci	struct wilc_priv *priv = &vif->priv;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	priv->p2p_listen_state = false;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	flush_workqueue(vif->wilc->hif_workqueue);
192462306a36Sopenharmony_ci	mutex_destroy(&priv->scan_req_lock);
192562306a36Sopenharmony_ci	ret = wilc_deinit(vif);
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (ret)
192862306a36Sopenharmony_ci		netdev_err(net, "Error while deinitializing host interface\n");
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
1931