18e84c258SEugene Krasnikov /*
28e84c258SEugene Krasnikov  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
38e84c258SEugene Krasnikov  *
48e84c258SEugene Krasnikov  * Permission to use, copy, modify, and/or distribute this software for any
58e84c258SEugene Krasnikov  * purpose with or without fee is hereby granted, provided that the above
68e84c258SEugene Krasnikov  * copyright notice and this permission notice appear in all copies.
78e84c258SEugene Krasnikov  *
88e84c258SEugene Krasnikov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98e84c258SEugene Krasnikov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108e84c258SEugene Krasnikov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
118e84c258SEugene Krasnikov  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128e84c258SEugene Krasnikov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
138e84c258SEugene Krasnikov  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
148e84c258SEugene Krasnikov  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158e84c258SEugene Krasnikov  */
168e84c258SEugene Krasnikov 
178e84c258SEugene Krasnikov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
188e84c258SEugene Krasnikov 
198e84c258SEugene Krasnikov #include <linux/module.h>
204bda7fafSPontus Fuchs #include <linux/firmware.h>
218e84c258SEugene Krasnikov #include <linux/platform_device.h>
228e84c258SEugene Krasnikov #include "wcn36xx.h"
238e84c258SEugene Krasnikov 
248e84c258SEugene Krasnikov unsigned int wcn36xx_dbg_mask;
258e84c258SEugene Krasnikov module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644);
268e84c258SEugene Krasnikov MODULE_PARM_DESC(debug_mask, "Debugging mask");
278e84c258SEugene Krasnikov 
288e84c258SEugene Krasnikov #define CHAN2G(_freq, _idx) { \
298e84c258SEugene Krasnikov 	.band = IEEE80211_BAND_2GHZ, \
308e84c258SEugene Krasnikov 	.center_freq = (_freq), \
318e84c258SEugene Krasnikov 	.hw_value = (_idx), \
328e84c258SEugene Krasnikov 	.max_power = 25, \
338e84c258SEugene Krasnikov }
348e84c258SEugene Krasnikov 
358e84c258SEugene Krasnikov #define CHAN5G(_freq, _idx) { \
368e84c258SEugene Krasnikov 	.band = IEEE80211_BAND_5GHZ, \
378e84c258SEugene Krasnikov 	.center_freq = (_freq), \
388e84c258SEugene Krasnikov 	.hw_value = (_idx), \
398e84c258SEugene Krasnikov 	.max_power = 25, \
408e84c258SEugene Krasnikov }
418e84c258SEugene Krasnikov 
428e84c258SEugene Krasnikov /* The wcn firmware expects channel values to matching
438e84c258SEugene Krasnikov  * their mnemonic values. So use these for .hw_value. */
448e84c258SEugene Krasnikov static struct ieee80211_channel wcn_2ghz_channels[] = {
458e84c258SEugene Krasnikov 	CHAN2G(2412, 1), /* Channel 1 */
468e84c258SEugene Krasnikov 	CHAN2G(2417, 2), /* Channel 2 */
478e84c258SEugene Krasnikov 	CHAN2G(2422, 3), /* Channel 3 */
488e84c258SEugene Krasnikov 	CHAN2G(2427, 4), /* Channel 4 */
498e84c258SEugene Krasnikov 	CHAN2G(2432, 5), /* Channel 5 */
508e84c258SEugene Krasnikov 	CHAN2G(2437, 6), /* Channel 6 */
518e84c258SEugene Krasnikov 	CHAN2G(2442, 7), /* Channel 7 */
528e84c258SEugene Krasnikov 	CHAN2G(2447, 8), /* Channel 8 */
538e84c258SEugene Krasnikov 	CHAN2G(2452, 9), /* Channel 9 */
548e84c258SEugene Krasnikov 	CHAN2G(2457, 10), /* Channel 10 */
558e84c258SEugene Krasnikov 	CHAN2G(2462, 11), /* Channel 11 */
568e84c258SEugene Krasnikov 	CHAN2G(2467, 12), /* Channel 12 */
578e84c258SEugene Krasnikov 	CHAN2G(2472, 13), /* Channel 13 */
588e84c258SEugene Krasnikov 	CHAN2G(2484, 14)  /* Channel 14 */
598e84c258SEugene Krasnikov 
608e84c258SEugene Krasnikov };
618e84c258SEugene Krasnikov 
628e84c258SEugene Krasnikov static struct ieee80211_channel wcn_5ghz_channels[] = {
638e84c258SEugene Krasnikov 	CHAN5G(5180, 36),
648e84c258SEugene Krasnikov 	CHAN5G(5200, 40),
658e84c258SEugene Krasnikov 	CHAN5G(5220, 44),
668e84c258SEugene Krasnikov 	CHAN5G(5240, 48),
678e84c258SEugene Krasnikov 	CHAN5G(5260, 52),
688e84c258SEugene Krasnikov 	CHAN5G(5280, 56),
698e84c258SEugene Krasnikov 	CHAN5G(5300, 60),
708e84c258SEugene Krasnikov 	CHAN5G(5320, 64),
718e84c258SEugene Krasnikov 	CHAN5G(5500, 100),
728e84c258SEugene Krasnikov 	CHAN5G(5520, 104),
738e84c258SEugene Krasnikov 	CHAN5G(5540, 108),
748e84c258SEugene Krasnikov 	CHAN5G(5560, 112),
758e84c258SEugene Krasnikov 	CHAN5G(5580, 116),
768e84c258SEugene Krasnikov 	CHAN5G(5600, 120),
778e84c258SEugene Krasnikov 	CHAN5G(5620, 124),
788e84c258SEugene Krasnikov 	CHAN5G(5640, 128),
798e84c258SEugene Krasnikov 	CHAN5G(5660, 132),
808e84c258SEugene Krasnikov 	CHAN5G(5700, 140),
818e84c258SEugene Krasnikov 	CHAN5G(5745, 149),
828e84c258SEugene Krasnikov 	CHAN5G(5765, 153),
838e84c258SEugene Krasnikov 	CHAN5G(5785, 157),
848e84c258SEugene Krasnikov 	CHAN5G(5805, 161),
858e84c258SEugene Krasnikov 	CHAN5G(5825, 165)
868e84c258SEugene Krasnikov };
878e84c258SEugene Krasnikov 
888e84c258SEugene Krasnikov #define RATE(_bitrate, _hw_rate, _flags) { \
898e84c258SEugene Krasnikov 	.bitrate        = (_bitrate),                   \
908e84c258SEugene Krasnikov 	.flags          = (_flags),                     \
918e84c258SEugene Krasnikov 	.hw_value       = (_hw_rate),                   \
928e84c258SEugene Krasnikov 	.hw_value_short = (_hw_rate)  \
938e84c258SEugene Krasnikov }
948e84c258SEugene Krasnikov 
958e84c258SEugene Krasnikov static struct ieee80211_rate wcn_2ghz_rates[] = {
968e84c258SEugene Krasnikov 	RATE(10, HW_RATE_INDEX_1MBPS, 0),
978e84c258SEugene Krasnikov 	RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
988e84c258SEugene Krasnikov 	RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
998e84c258SEugene Krasnikov 	RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
1008e84c258SEugene Krasnikov 	RATE(60, HW_RATE_INDEX_6MBPS, 0),
1018e84c258SEugene Krasnikov 	RATE(90, HW_RATE_INDEX_9MBPS, 0),
1028e84c258SEugene Krasnikov 	RATE(120, HW_RATE_INDEX_12MBPS, 0),
1038e84c258SEugene Krasnikov 	RATE(180, HW_RATE_INDEX_18MBPS, 0),
1048e84c258SEugene Krasnikov 	RATE(240, HW_RATE_INDEX_24MBPS, 0),
1058e84c258SEugene Krasnikov 	RATE(360, HW_RATE_INDEX_36MBPS, 0),
1068e84c258SEugene Krasnikov 	RATE(480, HW_RATE_INDEX_48MBPS, 0),
1078e84c258SEugene Krasnikov 	RATE(540, HW_RATE_INDEX_54MBPS, 0)
1088e84c258SEugene Krasnikov };
1098e84c258SEugene Krasnikov 
1108e84c258SEugene Krasnikov static struct ieee80211_rate wcn_5ghz_rates[] = {
1118e84c258SEugene Krasnikov 	RATE(60, HW_RATE_INDEX_6MBPS, 0),
1128e84c258SEugene Krasnikov 	RATE(90, HW_RATE_INDEX_9MBPS, 0),
1138e84c258SEugene Krasnikov 	RATE(120, HW_RATE_INDEX_12MBPS, 0),
1148e84c258SEugene Krasnikov 	RATE(180, HW_RATE_INDEX_18MBPS, 0),
1158e84c258SEugene Krasnikov 	RATE(240, HW_RATE_INDEX_24MBPS, 0),
1168e84c258SEugene Krasnikov 	RATE(360, HW_RATE_INDEX_36MBPS, 0),
1178e84c258SEugene Krasnikov 	RATE(480, HW_RATE_INDEX_48MBPS, 0),
1188e84c258SEugene Krasnikov 	RATE(540, HW_RATE_INDEX_54MBPS, 0)
1198e84c258SEugene Krasnikov };
1208e84c258SEugene Krasnikov 
1218e84c258SEugene Krasnikov static struct ieee80211_supported_band wcn_band_2ghz = {
1228e84c258SEugene Krasnikov 	.channels	= wcn_2ghz_channels,
1238e84c258SEugene Krasnikov 	.n_channels	= ARRAY_SIZE(wcn_2ghz_channels),
1248e84c258SEugene Krasnikov 	.bitrates	= wcn_2ghz_rates,
1258e84c258SEugene Krasnikov 	.n_bitrates	= ARRAY_SIZE(wcn_2ghz_rates),
1268e84c258SEugene Krasnikov 	.ht_cap		= {
1278e84c258SEugene Krasnikov 		.cap =	IEEE80211_HT_CAP_GRN_FLD |
1288e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SGI_20 |
1298e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_DSSSCCK40 |
1308e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_LSIG_TXOP_PROT,
1318e84c258SEugene Krasnikov 		.ht_supported = true,
1328e84c258SEugene Krasnikov 		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
1338e84c258SEugene Krasnikov 		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
1348e84c258SEugene Krasnikov 		.mcs = {
1358e84c258SEugene Krasnikov 			.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
1368e84c258SEugene Krasnikov 			.rx_highest = cpu_to_le16(72),
1378e84c258SEugene Krasnikov 			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
1388e84c258SEugene Krasnikov 		}
1398e84c258SEugene Krasnikov 	}
1408e84c258SEugene Krasnikov };
1418e84c258SEugene Krasnikov 
1428e84c258SEugene Krasnikov static struct ieee80211_supported_band wcn_band_5ghz = {
1438e84c258SEugene Krasnikov 	.channels	= wcn_5ghz_channels,
1448e84c258SEugene Krasnikov 	.n_channels	= ARRAY_SIZE(wcn_5ghz_channels),
1458e84c258SEugene Krasnikov 	.bitrates	= wcn_5ghz_rates,
1468e84c258SEugene Krasnikov 	.n_bitrates	= ARRAY_SIZE(wcn_5ghz_rates),
1478e84c258SEugene Krasnikov 	.ht_cap		= {
1488e84c258SEugene Krasnikov 		.cap =	IEEE80211_HT_CAP_GRN_FLD |
1498e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SGI_20 |
1508e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_DSSSCCK40 |
1518e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_LSIG_TXOP_PROT |
1528e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SGI_40 |
1538e84c258SEugene Krasnikov 			IEEE80211_HT_CAP_SUP_WIDTH_20_40,
1548e84c258SEugene Krasnikov 		.ht_supported = true,
1558e84c258SEugene Krasnikov 		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
1568e84c258SEugene Krasnikov 		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
1578e84c258SEugene Krasnikov 		.mcs = {
1588e84c258SEugene Krasnikov 			.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
1598e84c258SEugene Krasnikov 			.rx_highest = cpu_to_le16(72),
1608e84c258SEugene Krasnikov 			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
1618e84c258SEugene Krasnikov 		}
1628e84c258SEugene Krasnikov 	}
1638e84c258SEugene Krasnikov };
1648e84c258SEugene Krasnikov 
1658e84c258SEugene Krasnikov #ifdef CONFIG_PM
1668e84c258SEugene Krasnikov 
1678e84c258SEugene Krasnikov static const struct wiphy_wowlan_support wowlan_support = {
1688e84c258SEugene Krasnikov 	.flags = WIPHY_WOWLAN_ANY
1698e84c258SEugene Krasnikov };
1708e84c258SEugene Krasnikov 
1718e84c258SEugene Krasnikov #endif
1728e84c258SEugene Krasnikov 
1738e84c258SEugene Krasnikov static inline u8 get_sta_index(struct ieee80211_vif *vif,
1748e84c258SEugene Krasnikov 			       struct wcn36xx_sta *sta_priv)
1758e84c258SEugene Krasnikov {
1768e84c258SEugene Krasnikov 	return NL80211_IFTYPE_STATION == vif->type ?
1778e84c258SEugene Krasnikov 	       sta_priv->bss_sta_index :
1788e84c258SEugene Krasnikov 	       sta_priv->sta_index;
1798e84c258SEugene Krasnikov }
1808e84c258SEugene Krasnikov 
1812be6636aSPontus Fuchs static const char * const wcn36xx_caps_names[] = {
1822be6636aSPontus Fuchs 	"MCC",				/* 0 */
1832be6636aSPontus Fuchs 	"P2P",				/* 1 */
1842be6636aSPontus Fuchs 	"DOT11AC",			/* 2 */
1852be6636aSPontus Fuchs 	"SLM_SESSIONIZATION",		/* 3 */
1862be6636aSPontus Fuchs 	"DOT11AC_OPMODE",		/* 4 */
1872be6636aSPontus Fuchs 	"SAP32STA",			/* 5 */
1882be6636aSPontus Fuchs 	"TDLS",				/* 6 */
1892be6636aSPontus Fuchs 	"P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */
1902be6636aSPontus Fuchs 	"WLANACTIVE_OFFLOAD",		/* 8 */
1912be6636aSPontus Fuchs 	"BEACON_OFFLOAD",		/* 9 */
1922be6636aSPontus Fuchs 	"SCAN_OFFLOAD",			/* 10 */
1932be6636aSPontus Fuchs 	"ROAM_OFFLOAD",			/* 11 */
1942be6636aSPontus Fuchs 	"BCN_MISS_OFFLOAD",		/* 12 */
1952be6636aSPontus Fuchs 	"STA_POWERSAVE",		/* 13 */
1962be6636aSPontus Fuchs 	"STA_ADVANCED_PWRSAVE",		/* 14 */
1972be6636aSPontus Fuchs 	"AP_UAPSD",			/* 15 */
1982be6636aSPontus Fuchs 	"AP_DFS",			/* 16 */
1992be6636aSPontus Fuchs 	"BLOCKACK",			/* 17 */
2002be6636aSPontus Fuchs 	"PHY_ERR",			/* 18 */
2012be6636aSPontus Fuchs 	"BCN_FILTER",			/* 19 */
2022be6636aSPontus Fuchs 	"RTT",				/* 20 */
2032be6636aSPontus Fuchs 	"RATECTRL",			/* 21 */
2042be6636aSPontus Fuchs 	"WOW"				/* 22 */
2052be6636aSPontus Fuchs };
2062be6636aSPontus Fuchs 
2072be6636aSPontus Fuchs static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
2082be6636aSPontus Fuchs {
2092be6636aSPontus Fuchs 	if (x >= ARRAY_SIZE(wcn36xx_caps_names))
2102be6636aSPontus Fuchs 		return "UNKNOWN";
2112be6636aSPontus Fuchs 	return wcn36xx_caps_names[x];
2122be6636aSPontus Fuchs }
2132be6636aSPontus Fuchs 
2142be6636aSPontus Fuchs static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
2152be6636aSPontus Fuchs {
2162be6636aSPontus Fuchs 	int i;
2172be6636aSPontus Fuchs 
2182be6636aSPontus Fuchs 	for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
2192be6636aSPontus Fuchs 		if (get_feat_caps(wcn->fw_feat_caps, i))
2202be6636aSPontus Fuchs 			wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i));
2212be6636aSPontus Fuchs 	}
2222be6636aSPontus Fuchs }
2232be6636aSPontus Fuchs 
224f2ed5d24SPontus Fuchs static void wcn36xx_detect_chip_version(struct wcn36xx *wcn)
225f2ed5d24SPontus Fuchs {
226f2ed5d24SPontus Fuchs 	if (get_feat_caps(wcn->fw_feat_caps, DOT11AC)) {
227f2ed5d24SPontus Fuchs 		wcn36xx_info("Chip is 3680\n");
228f2ed5d24SPontus Fuchs 		wcn->chip_version = WCN36XX_CHIP_3680;
229f2ed5d24SPontus Fuchs 	} else {
230f2ed5d24SPontus Fuchs 		wcn36xx_info("Chip is 3660\n");
231f2ed5d24SPontus Fuchs 		wcn->chip_version = WCN36XX_CHIP_3660;
232f2ed5d24SPontus Fuchs 	}
233f2ed5d24SPontus Fuchs }
234f2ed5d24SPontus Fuchs 
2358e84c258SEugene Krasnikov static int wcn36xx_start(struct ieee80211_hw *hw)
2368e84c258SEugene Krasnikov {
2378e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
2388e84c258SEugene Krasnikov 	int ret;
2398e84c258SEugene Krasnikov 
2408e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n");
2418e84c258SEugene Krasnikov 
2428e84c258SEugene Krasnikov 	/* SMD initialization */
2438e84c258SEugene Krasnikov 	ret = wcn36xx_smd_open(wcn);
2448e84c258SEugene Krasnikov 	if (ret) {
2458e84c258SEugene Krasnikov 		wcn36xx_err("Failed to open smd channel: %d\n", ret);
2468e84c258SEugene Krasnikov 		goto out_err;
2478e84c258SEugene Krasnikov 	}
2488e84c258SEugene Krasnikov 
2498e84c258SEugene Krasnikov 	/* Allocate memory pools for Mgmt BD headers and Data BD headers */
2508e84c258SEugene Krasnikov 	ret = wcn36xx_dxe_allocate_mem_pools(wcn);
2518e84c258SEugene Krasnikov 	if (ret) {
2528e84c258SEugene Krasnikov 		wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret);
2538e84c258SEugene Krasnikov 		goto out_smd_close;
2548e84c258SEugene Krasnikov 	}
2558e84c258SEugene Krasnikov 
2568e84c258SEugene Krasnikov 	ret = wcn36xx_dxe_alloc_ctl_blks(wcn);
2578e84c258SEugene Krasnikov 	if (ret) {
2588e84c258SEugene Krasnikov 		wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret);
2598e84c258SEugene Krasnikov 		goto out_free_dxe_pool;
2608e84c258SEugene Krasnikov 	}
2618e84c258SEugene Krasnikov 
2628e84c258SEugene Krasnikov 	wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL);
2638e84c258SEugene Krasnikov 	if (!wcn->hal_buf) {
2648e84c258SEugene Krasnikov 		wcn36xx_err("Failed to allocate smd buf\n");
2658e84c258SEugene Krasnikov 		ret = -ENOMEM;
2668e84c258SEugene Krasnikov 		goto out_free_dxe_ctl;
2678e84c258SEugene Krasnikov 	}
2688e84c258SEugene Krasnikov 
2698e84c258SEugene Krasnikov 	ret = wcn36xx_smd_load_nv(wcn);
2708e84c258SEugene Krasnikov 	if (ret) {
2718e84c258SEugene Krasnikov 		wcn36xx_err("Failed to push NV to chip\n");
2728e84c258SEugene Krasnikov 		goto out_free_smd_buf;
2738e84c258SEugene Krasnikov 	}
2748e84c258SEugene Krasnikov 
2758e84c258SEugene Krasnikov 	ret = wcn36xx_smd_start(wcn);
2768e84c258SEugene Krasnikov 	if (ret) {
2778e84c258SEugene Krasnikov 		wcn36xx_err("Failed to start chip\n");
2788e84c258SEugene Krasnikov 		goto out_free_smd_buf;
2798e84c258SEugene Krasnikov 	}
2808e84c258SEugene Krasnikov 
281f2ed5d24SPontus Fuchs 	if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
282f2ed5d24SPontus Fuchs 		ret = wcn36xx_smd_feature_caps_exchange(wcn);
283f2ed5d24SPontus Fuchs 		if (ret)
284f2ed5d24SPontus Fuchs 			wcn36xx_warn("Exchange feature caps failed\n");
285f2ed5d24SPontus Fuchs 		else
286f2ed5d24SPontus Fuchs 			wcn36xx_feat_caps_info(wcn);
287f2ed5d24SPontus Fuchs 	}
288f2ed5d24SPontus Fuchs 
289f2ed5d24SPontus Fuchs 	wcn36xx_detect_chip_version(wcn);
29020a779edSPontus Fuchs 	wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST, 1);
291f2ed5d24SPontus Fuchs 
2928e84c258SEugene Krasnikov 	/* DMA channel initialization */
2938e84c258SEugene Krasnikov 	ret = wcn36xx_dxe_init(wcn);
2948e84c258SEugene Krasnikov 	if (ret) {
2958e84c258SEugene Krasnikov 		wcn36xx_err("DXE init failed\n");
2968e84c258SEugene Krasnikov 		goto out_smd_stop;
2978e84c258SEugene Krasnikov 	}
2988e84c258SEugene Krasnikov 
2998e84c258SEugene Krasnikov 	wcn36xx_debugfs_init(wcn);
3008e84c258SEugene Krasnikov 
3018e84c258SEugene Krasnikov 	INIT_LIST_HEAD(&wcn->vif_list);
3024b12462aSBob Copeland 	spin_lock_init(&wcn->dxe_lock);
3034b12462aSBob Copeland 
3048e84c258SEugene Krasnikov 	return 0;
3058e84c258SEugene Krasnikov 
3068e84c258SEugene Krasnikov out_smd_stop:
3078e84c258SEugene Krasnikov 	wcn36xx_smd_stop(wcn);
3088e84c258SEugene Krasnikov out_free_smd_buf:
3098e84c258SEugene Krasnikov 	kfree(wcn->hal_buf);
3108e84c258SEugene Krasnikov out_free_dxe_pool:
3118e84c258SEugene Krasnikov 	wcn36xx_dxe_free_mem_pools(wcn);
3128e84c258SEugene Krasnikov out_free_dxe_ctl:
3138e84c258SEugene Krasnikov 	wcn36xx_dxe_free_ctl_blks(wcn);
3148e84c258SEugene Krasnikov out_smd_close:
3158e84c258SEugene Krasnikov 	wcn36xx_smd_close(wcn);
3168e84c258SEugene Krasnikov out_err:
3178e84c258SEugene Krasnikov 	return ret;
3188e84c258SEugene Krasnikov }
3198e84c258SEugene Krasnikov 
3208e84c258SEugene Krasnikov static void wcn36xx_stop(struct ieee80211_hw *hw)
3218e84c258SEugene Krasnikov {
3228e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
3238e84c258SEugene Krasnikov 
3248e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
3258e84c258SEugene Krasnikov 
3268e84c258SEugene Krasnikov 	wcn36xx_debugfs_exit(wcn);
3278e84c258SEugene Krasnikov 	wcn36xx_smd_stop(wcn);
3288e84c258SEugene Krasnikov 	wcn36xx_dxe_deinit(wcn);
3298e84c258SEugene Krasnikov 	wcn36xx_smd_close(wcn);
3308e84c258SEugene Krasnikov 
3318e84c258SEugene Krasnikov 	wcn36xx_dxe_free_mem_pools(wcn);
3328e84c258SEugene Krasnikov 	wcn36xx_dxe_free_ctl_blks(wcn);
3338e84c258SEugene Krasnikov 
3348e84c258SEugene Krasnikov 	kfree(wcn->hal_buf);
3358e84c258SEugene Krasnikov }
3368e84c258SEugene Krasnikov 
3378e84c258SEugene Krasnikov static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
3388e84c258SEugene Krasnikov {
3398e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
3408e84c258SEugene Krasnikov 	struct ieee80211_vif *vif = NULL;
3418e84c258SEugene Krasnikov 	struct wcn36xx_vif *tmp;
3428e84c258SEugene Krasnikov 
3438e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
3448e84c258SEugene Krasnikov 
3458e84c258SEugene Krasnikov 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
3468e84c258SEugene Krasnikov 		int ch = WCN36XX_HW_CHANNEL(wcn);
3478e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
3488e84c258SEugene Krasnikov 			    ch);
3498e84c258SEugene Krasnikov 		list_for_each_entry(tmp, &wcn->vif_list, list) {
350ce75877fSPontus Fuchs 			vif = wcn36xx_priv_to_vif(tmp);
3518e84c258SEugene Krasnikov 			wcn36xx_smd_switch_channel(wcn, vif, ch);
3528e84c258SEugene Krasnikov 		}
3538e84c258SEugene Krasnikov 	}
3548e84c258SEugene Krasnikov 
3558e84c258SEugene Krasnikov 	return 0;
3568e84c258SEugene Krasnikov }
3578e84c258SEugene Krasnikov 
3588e84c258SEugene Krasnikov static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
3598e84c258SEugene Krasnikov 				     unsigned int changed,
3608e84c258SEugene Krasnikov 				     unsigned int *total, u64 multicast)
3618e84c258SEugene Krasnikov {
36220a779edSPontus Fuchs 	struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
36320a779edSPontus Fuchs 	struct wcn36xx *wcn = hw->priv;
36420a779edSPontus Fuchs 	struct wcn36xx_vif *tmp;
36520a779edSPontus Fuchs 	struct ieee80211_vif *vif = NULL;
36620a779edSPontus Fuchs 
3678e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
3688e84c258SEugene Krasnikov 
36920a779edSPontus Fuchs 	*total &= FIF_ALLMULTI;
37020a779edSPontus Fuchs 
37120a779edSPontus Fuchs 	fp = (void *)(unsigned long)multicast;
37220a779edSPontus Fuchs 	list_for_each_entry(tmp, &wcn->vif_list, list) {
37320a779edSPontus Fuchs 		vif = wcn36xx_priv_to_vif(tmp);
37420a779edSPontus Fuchs 
37520a779edSPontus Fuchs 		/* FW handles MC filtering only when connected as STA */
37620a779edSPontus Fuchs 		if (*total & FIF_ALLMULTI)
37720a779edSPontus Fuchs 			wcn36xx_smd_set_mc_list(wcn, vif, NULL);
37820a779edSPontus Fuchs 		else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
37920a779edSPontus Fuchs 			wcn36xx_smd_set_mc_list(wcn, vif, fp);
38020a779edSPontus Fuchs 	}
38120a779edSPontus Fuchs 	kfree(fp);
38220a779edSPontus Fuchs }
38320a779edSPontus Fuchs 
38420a779edSPontus Fuchs static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw,
38520a779edSPontus Fuchs 				     struct netdev_hw_addr_list *mc_list)
38620a779edSPontus Fuchs {
38720a779edSPontus Fuchs 	struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
38820a779edSPontus Fuchs 	struct netdev_hw_addr *ha;
38920a779edSPontus Fuchs 
39020a779edSPontus Fuchs 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n");
39120a779edSPontus Fuchs 	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
39220a779edSPontus Fuchs 	if (!fp) {
39320a779edSPontus Fuchs 		wcn36xx_err("Out of memory setting filters.\n");
39420a779edSPontus Fuchs 		return 0;
39520a779edSPontus Fuchs 	}
39620a779edSPontus Fuchs 
39720a779edSPontus Fuchs 	fp->mc_addr_count = 0;
39820a779edSPontus Fuchs 	/* update multicast filtering parameters */
39920a779edSPontus Fuchs 	if (netdev_hw_addr_list_count(mc_list) <=
40020a779edSPontus Fuchs 	    WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) {
40120a779edSPontus Fuchs 		netdev_hw_addr_list_for_each(ha, mc_list) {
40220a779edSPontus Fuchs 			memcpy(fp->mc_addr[fp->mc_addr_count],
40320a779edSPontus Fuchs 					ha->addr, ETH_ALEN);
40420a779edSPontus Fuchs 			fp->mc_addr_count++;
40520a779edSPontus Fuchs 		}
40620a779edSPontus Fuchs 	}
40720a779edSPontus Fuchs 
40820a779edSPontus Fuchs 	return (u64)(unsigned long)fp;
4098e84c258SEugene Krasnikov }
4108e84c258SEugene Krasnikov 
4118e84c258SEugene Krasnikov static void wcn36xx_tx(struct ieee80211_hw *hw,
4128e84c258SEugene Krasnikov 		       struct ieee80211_tx_control *control,
4138e84c258SEugene Krasnikov 		       struct sk_buff *skb)
4148e84c258SEugene Krasnikov {
4158e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
4168e84c258SEugene Krasnikov 	struct wcn36xx_sta *sta_priv = NULL;
4178e84c258SEugene Krasnikov 
4188e84c258SEugene Krasnikov 	if (control->sta)
419a92e4696SPontus Fuchs 		sta_priv = wcn36xx_sta_to_priv(control->sta);
4208e84c258SEugene Krasnikov 
4218e84c258SEugene Krasnikov 	if (wcn36xx_start_tx(wcn, sta_priv, skb))
4228e84c258SEugene Krasnikov 		ieee80211_free_txskb(wcn->hw, skb);
4238e84c258SEugene Krasnikov }
4248e84c258SEugene Krasnikov 
4258e84c258SEugene Krasnikov static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
4268e84c258SEugene Krasnikov 			   struct ieee80211_vif *vif,
4278e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta,
4288e84c258SEugene Krasnikov 			   struct ieee80211_key_conf *key_conf)
4298e84c258SEugene Krasnikov {
4308e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
431ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
43281c69263SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
4338e84c258SEugene Krasnikov 	int ret = 0;
4348e84c258SEugene Krasnikov 	u8 key[WLAN_MAX_KEY_LEN];
4358e84c258SEugene Krasnikov 
4368e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
4378e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
4388e84c258SEugene Krasnikov 		    cmd, key_conf->cipher, key_conf->keyidx,
4398e84c258SEugene Krasnikov 		    key_conf->keylen, key_conf->flags);
4408e84c258SEugene Krasnikov 	wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
4418e84c258SEugene Krasnikov 			 key_conf->key,
4428e84c258SEugene Krasnikov 			 key_conf->keylen);
4438e84c258SEugene Krasnikov 
4448e84c258SEugene Krasnikov 	switch (key_conf->cipher) {
4458e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_WEP40:
4468e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
4478e84c258SEugene Krasnikov 		break;
4488e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_WEP104:
4498e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
4508e84c258SEugene Krasnikov 		break;
4518e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_CCMP:
4528e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP;
4538e84c258SEugene Krasnikov 		break;
4548e84c258SEugene Krasnikov 	case WLAN_CIPHER_SUITE_TKIP:
4558e84c258SEugene Krasnikov 		vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP;
4568e84c258SEugene Krasnikov 		break;
4578e84c258SEugene Krasnikov 	default:
4588e84c258SEugene Krasnikov 		wcn36xx_err("Unsupported key type 0x%x\n",
4598e84c258SEugene Krasnikov 			      key_conf->cipher);
4608e84c258SEugene Krasnikov 		ret = -EOPNOTSUPP;
4618e84c258SEugene Krasnikov 		goto out;
4628e84c258SEugene Krasnikov 	}
4638e84c258SEugene Krasnikov 
4648e84c258SEugene Krasnikov 	switch (cmd) {
4658e84c258SEugene Krasnikov 	case SET_KEY:
4668e84c258SEugene Krasnikov 		if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) {
4678e84c258SEugene Krasnikov 			/*
4688e84c258SEugene Krasnikov 			 * Supplicant is sending key in the wrong order:
4698e84c258SEugene Krasnikov 			 * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b)
4708e84c258SEugene Krasnikov 			 * but HW expects it to be in the order as described in
4718e84c258SEugene Krasnikov 			 * IEEE 802.11 spec (see chapter 11.7) like this:
4728e84c258SEugene Krasnikov 			 * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
4738e84c258SEugene Krasnikov 			 */
4748e84c258SEugene Krasnikov 			memcpy(key, key_conf->key, 16);
4758e84c258SEugene Krasnikov 			memcpy(key + 16, key_conf->key + 24, 8);
4768e84c258SEugene Krasnikov 			memcpy(key + 24, key_conf->key + 16, 8);
4778e84c258SEugene Krasnikov 		} else {
4788e84c258SEugene Krasnikov 			memcpy(key, key_conf->key, key_conf->keylen);
4798e84c258SEugene Krasnikov 		}
4808e84c258SEugene Krasnikov 
4818e84c258SEugene Krasnikov 		if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
4828e84c258SEugene Krasnikov 			sta_priv->is_data_encrypted = true;
4838e84c258SEugene Krasnikov 			/* Reconfigure bss with encrypt_type */
4848e84c258SEugene Krasnikov 			if (NL80211_IFTYPE_STATION == vif->type)
4858e84c258SEugene Krasnikov 				wcn36xx_smd_config_bss(wcn,
4868e84c258SEugene Krasnikov 						       vif,
4878e84c258SEugene Krasnikov 						       sta,
4888e84c258SEugene Krasnikov 						       sta->addr,
4898e84c258SEugene Krasnikov 						       true);
4908e84c258SEugene Krasnikov 
4918e84c258SEugene Krasnikov 			wcn36xx_smd_set_stakey(wcn,
4928e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
4938e84c258SEugene Krasnikov 				key_conf->keyidx,
4948e84c258SEugene Krasnikov 				key_conf->keylen,
4958e84c258SEugene Krasnikov 				key,
4968e84c258SEugene Krasnikov 				get_sta_index(vif, sta_priv));
4978e84c258SEugene Krasnikov 		} else {
4988e84c258SEugene Krasnikov 			wcn36xx_smd_set_bsskey(wcn,
4998e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5008e84c258SEugene Krasnikov 				key_conf->keyidx,
5018e84c258SEugene Krasnikov 				key_conf->keylen,
5028e84c258SEugene Krasnikov 				key);
5038e84c258SEugene Krasnikov 			if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
5048e84c258SEugene Krasnikov 			    (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
5058e84c258SEugene Krasnikov 				sta_priv->is_data_encrypted = true;
5068e84c258SEugene Krasnikov 				wcn36xx_smd_set_stakey(wcn,
5078e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5088e84c258SEugene Krasnikov 					key_conf->keyidx,
5098e84c258SEugene Krasnikov 					key_conf->keylen,
5108e84c258SEugene Krasnikov 					key,
5118e84c258SEugene Krasnikov 					get_sta_index(vif, sta_priv));
5128e84c258SEugene Krasnikov 			}
5138e84c258SEugene Krasnikov 		}
5148e84c258SEugene Krasnikov 		break;
5158e84c258SEugene Krasnikov 	case DISABLE_KEY:
5168e84c258SEugene Krasnikov 		if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
5172716a8acSPontus Fuchs 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
5188e84c258SEugene Krasnikov 			wcn36xx_smd_remove_bsskey(wcn,
5198e84c258SEugene Krasnikov 				vif_priv->encrypt_type,
5208e84c258SEugene Krasnikov 				key_conf->keyidx);
5218e84c258SEugene Krasnikov 		} else {
5228e84c258SEugene Krasnikov 			sta_priv->is_data_encrypted = false;
5238e84c258SEugene Krasnikov 			/* do not remove key if disassociated */
5248e84c258SEugene Krasnikov 			if (sta_priv->aid)
5258e84c258SEugene Krasnikov 				wcn36xx_smd_remove_stakey(wcn,
5268e84c258SEugene Krasnikov 					vif_priv->encrypt_type,
5278e84c258SEugene Krasnikov 					key_conf->keyidx,
5288e84c258SEugene Krasnikov 					get_sta_index(vif, sta_priv));
5298e84c258SEugene Krasnikov 		}
5308e84c258SEugene Krasnikov 		break;
5318e84c258SEugene Krasnikov 	default:
5328e84c258SEugene Krasnikov 		wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
5338e84c258SEugene Krasnikov 		ret = -EOPNOTSUPP;
5348e84c258SEugene Krasnikov 		goto out;
5358e84c258SEugene Krasnikov 	}
5368e84c258SEugene Krasnikov 
5378e84c258SEugene Krasnikov out:
5388e84c258SEugene Krasnikov 	return ret;
5398e84c258SEugene Krasnikov }
5408e84c258SEugene Krasnikov 
541a344d677SJohannes Berg static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
542a344d677SJohannes Berg 				  struct ieee80211_vif *vif,
543a344d677SJohannes Berg 				  const u8 *mac_addr)
5448e84c258SEugene Krasnikov {
5458e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
5468e84c258SEugene Krasnikov 
5478e84c258SEugene Krasnikov 	wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN);
5488e84c258SEugene Krasnikov 	wcn36xx_smd_start_scan(wcn);
5498e84c258SEugene Krasnikov }
5508e84c258SEugene Krasnikov 
551a344d677SJohannes Berg static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
552a344d677SJohannes Berg 				     struct ieee80211_vif *vif)
5538e84c258SEugene Krasnikov {
5548e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
5558e84c258SEugene Krasnikov 
5568e84c258SEugene Krasnikov 	wcn36xx_smd_end_scan(wcn);
5578e84c258SEugene Krasnikov 	wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
5588e84c258SEugene Krasnikov }
5598e84c258SEugene Krasnikov 
5608e84c258SEugene Krasnikov static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
5618e84c258SEugene Krasnikov 					 enum ieee80211_band band)
5628e84c258SEugene Krasnikov {
5638e84c258SEugene Krasnikov 	int i, size;
5648e84c258SEugene Krasnikov 	u16 *rates_table;
565a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
5668e84c258SEugene Krasnikov 	u32 rates = sta->supp_rates[band];
5678e84c258SEugene Krasnikov 
5688e84c258SEugene Krasnikov 	memset(&sta_priv->supported_rates, 0,
5698e84c258SEugene Krasnikov 		sizeof(sta_priv->supported_rates));
5708e84c258SEugene Krasnikov 	sta_priv->supported_rates.op_rate_mode = STA_11n;
5718e84c258SEugene Krasnikov 
5728e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
5738e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.dsss_rates;
5748e84c258SEugene Krasnikov 	if (band == IEEE80211_BAND_2GHZ) {
5758e84c258SEugene Krasnikov 		for (i = 0; i < size; i++) {
5768e84c258SEugene Krasnikov 			if (rates & 0x01) {
5778e84c258SEugene Krasnikov 				rates_table[i] = wcn_2ghz_rates[i].hw_value;
5788e84c258SEugene Krasnikov 				rates = rates >> 1;
5798e84c258SEugene Krasnikov 			}
5808e84c258SEugene Krasnikov 		}
5818e84c258SEugene Krasnikov 	}
5828e84c258SEugene Krasnikov 
5838e84c258SEugene Krasnikov 	size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
5848e84c258SEugene Krasnikov 	rates_table = sta_priv->supported_rates.ofdm_rates;
5858e84c258SEugene Krasnikov 	for (i = 0; i < size; i++) {
5868e84c258SEugene Krasnikov 		if (rates & 0x01) {
5878e84c258SEugene Krasnikov 			rates_table[i] = wcn_5ghz_rates[i].hw_value;
5888e84c258SEugene Krasnikov 			rates = rates >> 1;
5898e84c258SEugene Krasnikov 		}
5908e84c258SEugene Krasnikov 	}
5918e84c258SEugene Krasnikov 
5928e84c258SEugene Krasnikov 	if (sta->ht_cap.ht_supported) {
5938e84c258SEugene Krasnikov 		BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
5948e84c258SEugene Krasnikov 			sizeof(sta_priv->supported_rates.supported_mcs_set));
5958e84c258SEugene Krasnikov 		memcpy(sta_priv->supported_rates.supported_mcs_set,
5968e84c258SEugene Krasnikov 		       sta->ht_cap.mcs.rx_mask,
5978e84c258SEugene Krasnikov 		       sizeof(sta->ht_cap.mcs.rx_mask));
5988e84c258SEugene Krasnikov 	}
5998e84c258SEugene Krasnikov }
6008e84c258SEugene Krasnikov void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
6018e84c258SEugene Krasnikov {
6028e84c258SEugene Krasnikov 	u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
6038e84c258SEugene Krasnikov 		HW_RATE_INDEX_6MBPS,
6048e84c258SEugene Krasnikov 		HW_RATE_INDEX_9MBPS,
6058e84c258SEugene Krasnikov 		HW_RATE_INDEX_12MBPS,
6068e84c258SEugene Krasnikov 		HW_RATE_INDEX_18MBPS,
6078e84c258SEugene Krasnikov 		HW_RATE_INDEX_24MBPS,
6088e84c258SEugene Krasnikov 		HW_RATE_INDEX_36MBPS,
6098e84c258SEugene Krasnikov 		HW_RATE_INDEX_48MBPS,
6108e84c258SEugene Krasnikov 		HW_RATE_INDEX_54MBPS
6118e84c258SEugene Krasnikov 	};
6128e84c258SEugene Krasnikov 	u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
6138e84c258SEugene Krasnikov 		HW_RATE_INDEX_1MBPS,
6148e84c258SEugene Krasnikov 		HW_RATE_INDEX_2MBPS,
6158e84c258SEugene Krasnikov 		HW_RATE_INDEX_5_5MBPS,
6168e84c258SEugene Krasnikov 		HW_RATE_INDEX_11MBPS
6178e84c258SEugene Krasnikov 	};
6188e84c258SEugene Krasnikov 
6198e84c258SEugene Krasnikov 	rates->op_rate_mode = STA_11n;
6208e84c258SEugene Krasnikov 	memcpy(rates->dsss_rates, dsss_rates,
6218e84c258SEugene Krasnikov 		sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
6228e84c258SEugene Krasnikov 	memcpy(rates->ofdm_rates, ofdm_rates,
6238e84c258SEugene Krasnikov 		sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
6248e84c258SEugene Krasnikov 	rates->supported_mcs_set[0] = 0xFF;
6258e84c258SEugene Krasnikov }
6268e84c258SEugene Krasnikov static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
6278e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif,
6288e84c258SEugene Krasnikov 				     struct ieee80211_bss_conf *bss_conf,
6298e84c258SEugene Krasnikov 				     u32 changed)
6308e84c258SEugene Krasnikov {
6318e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
6328e84c258SEugene Krasnikov 	struct sk_buff *skb = NULL;
6338e84c258SEugene Krasnikov 	u16 tim_off, tim_len;
6348e84c258SEugene Krasnikov 	enum wcn36xx_hal_link_state link_state;
635ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
6368e84c258SEugene Krasnikov 
6378e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
6388e84c258SEugene Krasnikov 		    vif, changed);
6398e84c258SEugene Krasnikov 
6408e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BEACON_INFO) {
6418e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
6428e84c258SEugene Krasnikov 			    "mac bss changed dtim period %d\n",
6438e84c258SEugene Krasnikov 			    bss_conf->dtim_period);
6448e84c258SEugene Krasnikov 
6458e84c258SEugene Krasnikov 		vif_priv->dtim_period = bss_conf->dtim_period;
6468e84c258SEugene Krasnikov 	}
6478e84c258SEugene Krasnikov 
6488e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_PS) {
6498e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
6508e84c258SEugene Krasnikov 			    "mac bss PS set %d\n",
6518e84c258SEugene Krasnikov 			    bss_conf->ps);
6528e84c258SEugene Krasnikov 		if (bss_conf->ps) {
6538e84c258SEugene Krasnikov 			wcn36xx_pmc_enter_bmps_state(wcn, vif);
6548e84c258SEugene Krasnikov 		} else {
6558e84c258SEugene Krasnikov 			wcn36xx_pmc_exit_bmps_state(wcn, vif);
6568e84c258SEugene Krasnikov 		}
6578e84c258SEugene Krasnikov 	}
6588e84c258SEugene Krasnikov 
6598e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_BSSID) {
6608e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
6618e84c258SEugene Krasnikov 			    bss_conf->bssid);
6628e84c258SEugene Krasnikov 
6638e84c258SEugene Krasnikov 		if (!is_zero_ether_addr(bss_conf->bssid)) {
6648e84c258SEugene Krasnikov 			vif_priv->is_joining = true;
66590023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
6668e84c258SEugene Krasnikov 			wcn36xx_smd_join(wcn, bss_conf->bssid,
6678e84c258SEugene Krasnikov 					 vif->addr, WCN36XX_HW_CHANNEL(wcn));
6688e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
6698e84c258SEugene Krasnikov 					       bss_conf->bssid, false);
6708e84c258SEugene Krasnikov 		} else {
6718e84c258SEugene Krasnikov 			vif_priv->is_joining = false;
6728e84c258SEugene Krasnikov 			wcn36xx_smd_delete_bss(wcn, vif);
6732716a8acSPontus Fuchs 			vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
6748e84c258SEugene Krasnikov 		}
6758e84c258SEugene Krasnikov 	}
6768e84c258SEugene Krasnikov 
6778e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_SSID) {
6788e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
6798e84c258SEugene Krasnikov 			    "mac bss changed ssid\n");
6808e84c258SEugene Krasnikov 		wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
6818e84c258SEugene Krasnikov 				 bss_conf->ssid, bss_conf->ssid_len);
6828e84c258SEugene Krasnikov 
6838e84c258SEugene Krasnikov 		vif_priv->ssid.length = bss_conf->ssid_len;
6848e84c258SEugene Krasnikov 		memcpy(&vif_priv->ssid.ssid,
6858e84c258SEugene Krasnikov 		       bss_conf->ssid,
6868e84c258SEugene Krasnikov 		       bss_conf->ssid_len);
6878e84c258SEugene Krasnikov 	}
6888e84c258SEugene Krasnikov 
6898e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_ASSOC) {
6908e84c258SEugene Krasnikov 		vif_priv->is_joining = false;
6918e84c258SEugene Krasnikov 		if (bss_conf->assoc) {
6928e84c258SEugene Krasnikov 			struct ieee80211_sta *sta;
6938e84c258SEugene Krasnikov 			struct wcn36xx_sta *sta_priv;
6948e84c258SEugene Krasnikov 
6958e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
6968e84c258SEugene Krasnikov 				    "mac assoc bss %pM vif %pM AID=%d\n",
6978e84c258SEugene Krasnikov 				     bss_conf->bssid,
6988e84c258SEugene Krasnikov 				     vif->addr,
6998e84c258SEugene Krasnikov 				     bss_conf->aid);
7008e84c258SEugene Krasnikov 
701043ce546SPontus Fuchs 			vif_priv->sta_assoc = true;
7028e84c258SEugene Krasnikov 			rcu_read_lock();
7038e84c258SEugene Krasnikov 			sta = ieee80211_find_sta(vif, bss_conf->bssid);
7048e84c258SEugene Krasnikov 			if (!sta) {
7058e84c258SEugene Krasnikov 				wcn36xx_err("sta %pM is not found\n",
7068e84c258SEugene Krasnikov 					      bss_conf->bssid);
7078e84c258SEugene Krasnikov 				rcu_read_unlock();
7088e84c258SEugene Krasnikov 				goto out;
7098e84c258SEugene Krasnikov 			}
710a92e4696SPontus Fuchs 			sta_priv = wcn36xx_sta_to_priv(sta);
7118e84c258SEugene Krasnikov 
7128e84c258SEugene Krasnikov 			wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
7138e84c258SEugene Krasnikov 
7148e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
7158e84c258SEugene Krasnikov 				vif->addr,
7168e84c258SEugene Krasnikov 				WCN36XX_HAL_LINK_POSTASSOC_STATE);
7178e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, sta,
7188e84c258SEugene Krasnikov 					       bss_conf->bssid,
7198e84c258SEugene Krasnikov 					       true);
7208e84c258SEugene Krasnikov 			sta_priv->aid = bss_conf->aid;
7218e84c258SEugene Krasnikov 			/*
7228e84c258SEugene Krasnikov 			 * config_sta must be called from  because this is the
7238e84c258SEugene Krasnikov 			 * place where AID is available.
7248e84c258SEugene Krasnikov 			 */
7258e84c258SEugene Krasnikov 			wcn36xx_smd_config_sta(wcn, vif, sta);
7268e84c258SEugene Krasnikov 			rcu_read_unlock();
7278e84c258SEugene Krasnikov 		} else {
7288e84c258SEugene Krasnikov 			wcn36xx_dbg(WCN36XX_DBG_MAC,
7298e84c258SEugene Krasnikov 				    "disassociated bss %pM vif %pM AID=%d\n",
7308e84c258SEugene Krasnikov 				    bss_conf->bssid,
7318e84c258SEugene Krasnikov 				    vif->addr,
7328e84c258SEugene Krasnikov 				    bss_conf->aid);
733043ce546SPontus Fuchs 			vif_priv->sta_assoc = false;
7348e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn,
7358e84c258SEugene Krasnikov 						bss_conf->bssid,
7368e84c258SEugene Krasnikov 						vif->addr,
7378e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
7388e84c258SEugene Krasnikov 		}
7398e84c258SEugene Krasnikov 	}
7408e84c258SEugene Krasnikov 
7418e84c258SEugene Krasnikov 	if (changed & BSS_CHANGED_AP_PROBE_RESP) {
7428e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
7438e84c258SEugene Krasnikov 		skb = ieee80211_proberesp_get(hw, vif);
7448e84c258SEugene Krasnikov 		if (!skb) {
7458e84c258SEugene Krasnikov 			wcn36xx_err("failed to alloc probereq skb\n");
7468e84c258SEugene Krasnikov 			goto out;
7478e84c258SEugene Krasnikov 		}
7488e84c258SEugene Krasnikov 
7498e84c258SEugene Krasnikov 		wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
7508e84c258SEugene Krasnikov 		dev_kfree_skb(skb);
7518e84c258SEugene Krasnikov 	}
7528e84c258SEugene Krasnikov 
753b3e3f871SChun-Yeow Yeoh 	if (changed & BSS_CHANGED_BEACON_ENABLED ||
754b3e3f871SChun-Yeow Yeoh 	    changed & BSS_CHANGED_BEACON) {
7558e84c258SEugene Krasnikov 		wcn36xx_dbg(WCN36XX_DBG_MAC,
7568e84c258SEugene Krasnikov 			    "mac bss changed beacon enabled %d\n",
7578e84c258SEugene Krasnikov 			    bss_conf->enable_beacon);
7588e84c258SEugene Krasnikov 
7598e84c258SEugene Krasnikov 		if (bss_conf->enable_beacon) {
760908628dbSPontus Fuchs 			vif_priv->dtim_period = bss_conf->dtim_period;
76190023c03SPontus Fuchs 			vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
7628e84c258SEugene Krasnikov 			wcn36xx_smd_config_bss(wcn, vif, NULL,
7638e84c258SEugene Krasnikov 					       vif->addr, false);
7648e84c258SEugene Krasnikov 			skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
7658e84c258SEugene Krasnikov 						       &tim_len);
7668e84c258SEugene Krasnikov 			if (!skb) {
7678e84c258SEugene Krasnikov 				wcn36xx_err("failed to alloc beacon skb\n");
7688e84c258SEugene Krasnikov 				goto out;
7698e84c258SEugene Krasnikov 			}
7708e84c258SEugene Krasnikov 			wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
7718e84c258SEugene Krasnikov 			dev_kfree_skb(skb);
7728e84c258SEugene Krasnikov 
7738e84c258SEugene Krasnikov 			if (vif->type == NL80211_IFTYPE_ADHOC ||
7748e84c258SEugene Krasnikov 			    vif->type == NL80211_IFTYPE_MESH_POINT)
7758e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_IBSS_STATE;
7768e84c258SEugene Krasnikov 			else
7778e84c258SEugene Krasnikov 				link_state = WCN36XX_HAL_LINK_AP_STATE;
7788e84c258SEugene Krasnikov 
7798e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
7808e84c258SEugene Krasnikov 						link_state);
7818e84c258SEugene Krasnikov 		} else {
7828e84c258SEugene Krasnikov 			wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
7838e84c258SEugene Krasnikov 						WCN36XX_HAL_LINK_IDLE_STATE);
7848e84c258SEugene Krasnikov 			wcn36xx_smd_delete_bss(wcn, vif);
7858e84c258SEugene Krasnikov 		}
7868e84c258SEugene Krasnikov 	}
7878e84c258SEugene Krasnikov out:
7888e84c258SEugene Krasnikov 	return;
7898e84c258SEugene Krasnikov }
7908e84c258SEugene Krasnikov 
7918e84c258SEugene Krasnikov /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
7928e84c258SEugene Krasnikov static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
7938e84c258SEugene Krasnikov {
7948e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
7958e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
7968e84c258SEugene Krasnikov 
7978e84c258SEugene Krasnikov 	wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
7988e84c258SEugene Krasnikov 	return 0;
7998e84c258SEugene Krasnikov }
8008e84c258SEugene Krasnikov 
8018e84c258SEugene Krasnikov static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
8028e84c258SEugene Krasnikov 				     struct ieee80211_vif *vif)
8038e84c258SEugene Krasnikov {
8048e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
805ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
8068e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
8078e84c258SEugene Krasnikov 
8088e84c258SEugene Krasnikov 	list_del(&vif_priv->list);
8098e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta_self(wcn, vif->addr);
8108e84c258SEugene Krasnikov }
8118e84c258SEugene Krasnikov 
8128e84c258SEugene Krasnikov static int wcn36xx_add_interface(struct ieee80211_hw *hw,
8138e84c258SEugene Krasnikov 				 struct ieee80211_vif *vif)
8148e84c258SEugene Krasnikov {
8158e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
816ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
8178e84c258SEugene Krasnikov 
8188e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
8198e84c258SEugene Krasnikov 		    vif, vif->type);
8208e84c258SEugene Krasnikov 
8218e84c258SEugene Krasnikov 	if (!(NL80211_IFTYPE_STATION == vif->type ||
8228e84c258SEugene Krasnikov 	      NL80211_IFTYPE_AP == vif->type ||
8238e84c258SEugene Krasnikov 	      NL80211_IFTYPE_ADHOC == vif->type ||
8248e84c258SEugene Krasnikov 	      NL80211_IFTYPE_MESH_POINT == vif->type)) {
8258e84c258SEugene Krasnikov 		wcn36xx_warn("Unsupported interface type requested: %d\n",
8268e84c258SEugene Krasnikov 			     vif->type);
8278e84c258SEugene Krasnikov 		return -EOPNOTSUPP;
8288e84c258SEugene Krasnikov 	}
8298e84c258SEugene Krasnikov 
8308e84c258SEugene Krasnikov 	list_add(&vif_priv->list, &wcn->vif_list);
8318e84c258SEugene Krasnikov 	wcn36xx_smd_add_sta_self(wcn, vif);
8328e84c258SEugene Krasnikov 
8338e84c258SEugene Krasnikov 	return 0;
8348e84c258SEugene Krasnikov }
8358e84c258SEugene Krasnikov 
8368e84c258SEugene Krasnikov static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
8378e84c258SEugene Krasnikov 			   struct ieee80211_sta *sta)
8388e84c258SEugene Krasnikov {
8398e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
840ce75877fSPontus Fuchs 	struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
841a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
8428e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
8438e84c258SEugene Krasnikov 		    vif, sta->addr);
8448e84c258SEugene Krasnikov 
845e26dc173SBob Copeland 	spin_lock_init(&sta_priv->ampdu_lock);
8468e84c258SEugene Krasnikov 	sta_priv->vif = vif_priv;
8478e84c258SEugene Krasnikov 	/*
8488e84c258SEugene Krasnikov 	 * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
8498e84c258SEugene Krasnikov 	 * at this stage AID is not available yet.
8508e84c258SEugene Krasnikov 	 */
8518e84c258SEugene Krasnikov 	if (NL80211_IFTYPE_STATION != vif->type) {
8528e84c258SEugene Krasnikov 		wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
8538e84c258SEugene Krasnikov 		sta_priv->aid = sta->aid;
8548e84c258SEugene Krasnikov 		wcn36xx_smd_config_sta(wcn, vif, sta);
8558e84c258SEugene Krasnikov 	}
8568e84c258SEugene Krasnikov 	return 0;
8578e84c258SEugene Krasnikov }
8588e84c258SEugene Krasnikov 
8598e84c258SEugene Krasnikov static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
8608e84c258SEugene Krasnikov 			      struct ieee80211_vif *vif,
8618e84c258SEugene Krasnikov 			      struct ieee80211_sta *sta)
8628e84c258SEugene Krasnikov {
8638e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
864a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
8658e84c258SEugene Krasnikov 
8668e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
8678e84c258SEugene Krasnikov 		    vif, sta->addr, sta_priv->sta_index);
8688e84c258SEugene Krasnikov 
8698e84c258SEugene Krasnikov 	wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
8708e84c258SEugene Krasnikov 	sta_priv->vif = NULL;
8718e84c258SEugene Krasnikov 	return 0;
8728e84c258SEugene Krasnikov }
8738e84c258SEugene Krasnikov 
8748e84c258SEugene Krasnikov #ifdef CONFIG_PM
8758e84c258SEugene Krasnikov 
8768e84c258SEugene Krasnikov static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
8778e84c258SEugene Krasnikov {
8788e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
8798e84c258SEugene Krasnikov 
8808e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
8818e84c258SEugene Krasnikov 
8828e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
8838e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, true);
8848e84c258SEugene Krasnikov 	return 0;
8858e84c258SEugene Krasnikov }
8868e84c258SEugene Krasnikov 
8878e84c258SEugene Krasnikov static int wcn36xx_resume(struct ieee80211_hw *hw)
8888e84c258SEugene Krasnikov {
8898e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
8908e84c258SEugene Krasnikov 
8918e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
8928e84c258SEugene Krasnikov 
8938e84c258SEugene Krasnikov 	flush_workqueue(wcn->hal_ind_wq);
8948e84c258SEugene Krasnikov 	wcn36xx_smd_set_power_params(wcn, false);
8958e84c258SEugene Krasnikov 	return 0;
8968e84c258SEugene Krasnikov }
8978e84c258SEugene Krasnikov 
8988e84c258SEugene Krasnikov #endif
8998e84c258SEugene Krasnikov 
9008e84c258SEugene Krasnikov static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
9018e84c258SEugene Krasnikov 		    struct ieee80211_vif *vif,
90250ea05efSSara Sharon 		    struct ieee80211_ampdu_params *params)
9038e84c258SEugene Krasnikov {
9048e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
905a92e4696SPontus Fuchs 	struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta);
90650ea05efSSara Sharon 	struct ieee80211_sta *sta = params->sta;
90750ea05efSSara Sharon 	enum ieee80211_ampdu_mlme_action action = params->action;
90850ea05efSSara Sharon 	u16 tid = params->tid;
90950ea05efSSara Sharon 	u16 *ssn = &params->ssn;
9108e84c258SEugene Krasnikov 
9118e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
9128e84c258SEugene Krasnikov 		    action, tid);
9138e84c258SEugene Krasnikov 
9148e84c258SEugene Krasnikov 	switch (action) {
9158e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_START:
9168e84c258SEugene Krasnikov 		sta_priv->tid = tid;
9178e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
9188e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
9198e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba(wcn);
9208e84c258SEugene Krasnikov 		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
9218e84c258SEugene Krasnikov 		break;
9228e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_RX_STOP:
9238e84c258SEugene Krasnikov 		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
9248e84c258SEugene Krasnikov 		break;
9258e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_START:
926e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
927e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
928e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
929e26dc173SBob Copeland 
9308e84c258SEugene Krasnikov 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
9318e84c258SEugene Krasnikov 		break;
9328e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_OPERATIONAL:
933e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
934e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
935e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
936e26dc173SBob Copeland 
9378e84c258SEugene Krasnikov 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
9388e84c258SEugene Krasnikov 			get_sta_index(vif, sta_priv));
9398e84c258SEugene Krasnikov 		break;
9408e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
9418e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
9428e84c258SEugene Krasnikov 	case IEEE80211_AMPDU_TX_STOP_CONT:
943e26dc173SBob Copeland 		spin_lock_bh(&sta_priv->ampdu_lock);
944e26dc173SBob Copeland 		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
945e26dc173SBob Copeland 		spin_unlock_bh(&sta_priv->ampdu_lock);
946e26dc173SBob Copeland 
9478e84c258SEugene Krasnikov 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
9488e84c258SEugene Krasnikov 		break;
9498e84c258SEugene Krasnikov 	default:
9508e84c258SEugene Krasnikov 		wcn36xx_err("Unknown AMPDU action\n");
9518e84c258SEugene Krasnikov 	}
9528e84c258SEugene Krasnikov 
9538e84c258SEugene Krasnikov 	return 0;
9548e84c258SEugene Krasnikov }
9558e84c258SEugene Krasnikov 
9568e84c258SEugene Krasnikov static const struct ieee80211_ops wcn36xx_ops = {
9578e84c258SEugene Krasnikov 	.start			= wcn36xx_start,
9588e84c258SEugene Krasnikov 	.stop			= wcn36xx_stop,
9598e84c258SEugene Krasnikov 	.add_interface		= wcn36xx_add_interface,
9608e84c258SEugene Krasnikov 	.remove_interface	= wcn36xx_remove_interface,
9618e84c258SEugene Krasnikov #ifdef CONFIG_PM
9628e84c258SEugene Krasnikov 	.suspend		= wcn36xx_suspend,
9638e84c258SEugene Krasnikov 	.resume			= wcn36xx_resume,
9648e84c258SEugene Krasnikov #endif
9658e84c258SEugene Krasnikov 	.config			= wcn36xx_config,
96620a779edSPontus Fuchs 	.prepare_multicast	= wcn36xx_prepare_multicast,
9678e84c258SEugene Krasnikov 	.configure_filter       = wcn36xx_configure_filter,
9688e84c258SEugene Krasnikov 	.tx			= wcn36xx_tx,
9698e84c258SEugene Krasnikov 	.set_key		= wcn36xx_set_key,
9708e84c258SEugene Krasnikov 	.sw_scan_start		= wcn36xx_sw_scan_start,
9718e84c258SEugene Krasnikov 	.sw_scan_complete	= wcn36xx_sw_scan_complete,
9728e84c258SEugene Krasnikov 	.bss_info_changed	= wcn36xx_bss_info_changed,
9738e84c258SEugene Krasnikov 	.set_rts_threshold	= wcn36xx_set_rts_threshold,
9748e84c258SEugene Krasnikov 	.sta_add		= wcn36xx_sta_add,
9758e84c258SEugene Krasnikov 	.sta_remove		= wcn36xx_sta_remove,
9768e84c258SEugene Krasnikov 	.ampdu_action		= wcn36xx_ampdu_action,
9778e84c258SEugene Krasnikov };
9788e84c258SEugene Krasnikov 
9798e84c258SEugene Krasnikov static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
9808e84c258SEugene Krasnikov {
9818e84c258SEugene Krasnikov 	int ret = 0;
9828e84c258SEugene Krasnikov 
9838e84c258SEugene Krasnikov 	static const u32 cipher_suites[] = {
9848e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP40,
9858e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_WEP104,
9868e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_TKIP,
9878e84c258SEugene Krasnikov 		WLAN_CIPHER_SUITE_CCMP,
9888e84c258SEugene Krasnikov 	};
9898e84c258SEugene Krasnikov 
99030686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY);
99130686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION);
99230686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR);
99330686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
99430686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
99530686bf7SJohannes Berg 	ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
9968e84c258SEugene Krasnikov 
9978e84c258SEugene Krasnikov 	wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
9988e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_AP) |
9998e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_ADHOC) |
10008e84c258SEugene Krasnikov 		BIT(NL80211_IFTYPE_MESH_POINT);
10018e84c258SEugene Krasnikov 
10028e84c258SEugene Krasnikov 	wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz;
10038e84c258SEugene Krasnikov 	wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz;
10048e84c258SEugene Krasnikov 
10058e84c258SEugene Krasnikov 	wcn->hw->wiphy->cipher_suites = cipher_suites;
10068e84c258SEugene Krasnikov 	wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
10078e84c258SEugene Krasnikov 
10088e84c258SEugene Krasnikov 	wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
10098e84c258SEugene Krasnikov 
10108e84c258SEugene Krasnikov #ifdef CONFIG_PM
10118e84c258SEugene Krasnikov 	wcn->hw->wiphy->wowlan = &wowlan_support;
10128e84c258SEugene Krasnikov #endif
10138e84c258SEugene Krasnikov 
10148e84c258SEugene Krasnikov 	wcn->hw->max_listen_interval = 200;
10158e84c258SEugene Krasnikov 
10168e84c258SEugene Krasnikov 	wcn->hw->queues = 4;
10178e84c258SEugene Krasnikov 
10188e84c258SEugene Krasnikov 	SET_IEEE80211_DEV(wcn->hw, wcn->dev);
10198e84c258SEugene Krasnikov 
10208e84c258SEugene Krasnikov 	wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
10218e84c258SEugene Krasnikov 	wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
10228e84c258SEugene Krasnikov 
10238e84c258SEugene Krasnikov 	return ret;
10248e84c258SEugene Krasnikov }
10258e84c258SEugene Krasnikov 
10268e84c258SEugene Krasnikov static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
10278e84c258SEugene Krasnikov 					  struct platform_device *pdev)
10288e84c258SEugene Krasnikov {
10298e84c258SEugene Krasnikov 	struct resource *res;
10308e84c258SEugene Krasnikov 	/* Set TX IRQ */
10318e84c258SEugene Krasnikov 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10328e84c258SEugene Krasnikov 					   "wcnss_wlantx_irq");
10338e84c258SEugene Krasnikov 	if (!res) {
10348e84c258SEugene Krasnikov 		wcn36xx_err("failed to get tx_irq\n");
10358e84c258SEugene Krasnikov 		return -ENOENT;
10368e84c258SEugene Krasnikov 	}
10378e84c258SEugene Krasnikov 	wcn->tx_irq = res->start;
10388e84c258SEugene Krasnikov 
10398e84c258SEugene Krasnikov 	/* Set RX IRQ */
10408e84c258SEugene Krasnikov 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10418e84c258SEugene Krasnikov 					   "wcnss_wlanrx_irq");
10428e84c258SEugene Krasnikov 	if (!res) {
10438e84c258SEugene Krasnikov 		wcn36xx_err("failed to get rx_irq\n");
10448e84c258SEugene Krasnikov 		return -ENOENT;
10458e84c258SEugene Krasnikov 	}
10468e84c258SEugene Krasnikov 	wcn->rx_irq = res->start;
10478e84c258SEugene Krasnikov 
10488e84c258SEugene Krasnikov 	/* Map the memory */
10498e84c258SEugene Krasnikov 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
10508e84c258SEugene Krasnikov 						 "wcnss_mmio");
10518e84c258SEugene Krasnikov 	if (!res) {
10528e84c258SEugene Krasnikov 		wcn36xx_err("failed to get mmio\n");
10538e84c258SEugene Krasnikov 		return -ENOENT;
10548e84c258SEugene Krasnikov 	}
10558e84c258SEugene Krasnikov 	wcn->mmio = ioremap(res->start, resource_size(res));
10568e84c258SEugene Krasnikov 	if (!wcn->mmio) {
10578e84c258SEugene Krasnikov 		wcn36xx_err("failed to map io memory\n");
10588e84c258SEugene Krasnikov 		return -ENOMEM;
10598e84c258SEugene Krasnikov 	}
10608e84c258SEugene Krasnikov 	return 0;
10618e84c258SEugene Krasnikov }
10628e84c258SEugene Krasnikov 
10638e84c258SEugene Krasnikov static int wcn36xx_probe(struct platform_device *pdev)
10648e84c258SEugene Krasnikov {
10658e84c258SEugene Krasnikov 	struct ieee80211_hw *hw;
10668e84c258SEugene Krasnikov 	struct wcn36xx *wcn;
10678e84c258SEugene Krasnikov 	int ret;
10688e84c258SEugene Krasnikov 	u8 addr[ETH_ALEN];
10698e84c258SEugene Krasnikov 
10708e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
10718e84c258SEugene Krasnikov 
10728e84c258SEugene Krasnikov 	hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
10738e84c258SEugene Krasnikov 	if (!hw) {
10748e84c258SEugene Krasnikov 		wcn36xx_err("failed to alloc hw\n");
10758e84c258SEugene Krasnikov 		ret = -ENOMEM;
10768e84c258SEugene Krasnikov 		goto out_err;
10778e84c258SEugene Krasnikov 	}
10788e84c258SEugene Krasnikov 	platform_set_drvdata(pdev, hw);
10798e84c258SEugene Krasnikov 	wcn = hw->priv;
10808e84c258SEugene Krasnikov 	wcn->hw = hw;
10818e84c258SEugene Krasnikov 	wcn->dev = &pdev->dev;
10828e84c258SEugene Krasnikov 	wcn->ctrl_ops = pdev->dev.platform_data;
10838e84c258SEugene Krasnikov 
10848e84c258SEugene Krasnikov 	mutex_init(&wcn->hal_mutex);
10858e84c258SEugene Krasnikov 
10868e84c258SEugene Krasnikov 	if (!wcn->ctrl_ops->get_hw_mac(addr)) {
10878e84c258SEugene Krasnikov 		wcn36xx_info("mac address: %pM\n", addr);
10888e84c258SEugene Krasnikov 		SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
10898e84c258SEugene Krasnikov 	}
10908e84c258SEugene Krasnikov 
10918e84c258SEugene Krasnikov 	ret = wcn36xx_platform_get_resources(wcn, pdev);
10928e84c258SEugene Krasnikov 	if (ret)
10938e84c258SEugene Krasnikov 		goto out_wq;
10948e84c258SEugene Krasnikov 
10958e84c258SEugene Krasnikov 	wcn36xx_init_ieee80211(wcn);
10968e84c258SEugene Krasnikov 	ret = ieee80211_register_hw(wcn->hw);
10978e84c258SEugene Krasnikov 	if (ret)
10988e84c258SEugene Krasnikov 		goto out_unmap;
10998e84c258SEugene Krasnikov 
11008e84c258SEugene Krasnikov 	return 0;
11018e84c258SEugene Krasnikov 
11028e84c258SEugene Krasnikov out_unmap:
11038e84c258SEugene Krasnikov 	iounmap(wcn->mmio);
11048e84c258SEugene Krasnikov out_wq:
11058e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
11068e84c258SEugene Krasnikov out_err:
11078e84c258SEugene Krasnikov 	return ret;
11088e84c258SEugene Krasnikov }
11098e84c258SEugene Krasnikov static int wcn36xx_remove(struct platform_device *pdev)
11108e84c258SEugene Krasnikov {
11118e84c258SEugene Krasnikov 	struct ieee80211_hw *hw = platform_get_drvdata(pdev);
11128e84c258SEugene Krasnikov 	struct wcn36xx *wcn = hw->priv;
11138e84c258SEugene Krasnikov 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
11148e84c258SEugene Krasnikov 
11154bda7fafSPontus Fuchs 	release_firmware(wcn->nv);
11168e84c258SEugene Krasnikov 	mutex_destroy(&wcn->hal_mutex);
11178e84c258SEugene Krasnikov 
11188e84c258SEugene Krasnikov 	ieee80211_unregister_hw(hw);
11198e84c258SEugene Krasnikov 	iounmap(wcn->mmio);
11208e84c258SEugene Krasnikov 	ieee80211_free_hw(hw);
11218e84c258SEugene Krasnikov 
11228e84c258SEugene Krasnikov 	return 0;
11238e84c258SEugene Krasnikov }
11248e84c258SEugene Krasnikov static const struct platform_device_id wcn36xx_platform_id_table[] = {
11258e84c258SEugene Krasnikov 	{
11268e84c258SEugene Krasnikov 		.name = "wcn36xx",
11278e84c258SEugene Krasnikov 		.driver_data = 0
11288e84c258SEugene Krasnikov 	},
11298e84c258SEugene Krasnikov 	{}
11308e84c258SEugene Krasnikov };
11318e84c258SEugene Krasnikov MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table);
11328e84c258SEugene Krasnikov 
11338e84c258SEugene Krasnikov static struct platform_driver wcn36xx_driver = {
11348e84c258SEugene Krasnikov 	.probe      = wcn36xx_probe,
11358e84c258SEugene Krasnikov 	.remove     = wcn36xx_remove,
11368e84c258SEugene Krasnikov 	.driver         = {
11378e84c258SEugene Krasnikov 		.name   = "wcn36xx",
11388e84c258SEugene Krasnikov 	},
11398e84c258SEugene Krasnikov 	.id_table    = wcn36xx_platform_id_table,
11408e84c258SEugene Krasnikov };
11418e84c258SEugene Krasnikov 
11428e84c258SEugene Krasnikov static int __init wcn36xx_init(void)
11438e84c258SEugene Krasnikov {
11448e84c258SEugene Krasnikov 	platform_driver_register(&wcn36xx_driver);
11458e84c258SEugene Krasnikov 	return 0;
11468e84c258SEugene Krasnikov }
11478e84c258SEugene Krasnikov module_init(wcn36xx_init);
11488e84c258SEugene Krasnikov 
11498e84c258SEugene Krasnikov static void __exit wcn36xx_exit(void)
11508e84c258SEugene Krasnikov {
11518e84c258SEugene Krasnikov 	platform_driver_unregister(&wcn36xx_driver);
11528e84c258SEugene Krasnikov }
11538e84c258SEugene Krasnikov module_exit(wcn36xx_exit);
11548e84c258SEugene Krasnikov 
11558e84c258SEugene Krasnikov MODULE_LICENSE("Dual BSD/GPL");
11568e84c258SEugene Krasnikov MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
11578e84c258SEugene Krasnikov MODULE_FIRMWARE(WLAN_NV_FILE);
1158