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> 2205ddce49SBjorn Andersson #include <linux/of_address.h> 2305ddce49SBjorn Andersson #include <linux/of_device.h> 24f303a931SBjorn Andersson #include <linux/of_irq.h> 255052de8dSBjorn Andersson #include <linux/rpmsg.h> 26f303a931SBjorn Andersson #include <linux/soc/qcom/smem_state.h> 27f303a931SBjorn Andersson #include <linux/soc/qcom/wcnss_ctrl.h> 288e84c258SEugene Krasnikov #include "wcn36xx.h" 298e84c258SEugene Krasnikov 308e84c258SEugene Krasnikov unsigned int wcn36xx_dbg_mask; 318e84c258SEugene Krasnikov module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644); 328e84c258SEugene Krasnikov MODULE_PARM_DESC(debug_mask, "Debugging mask"); 338e84c258SEugene Krasnikov 348e84c258SEugene Krasnikov #define CHAN2G(_freq, _idx) { \ 3557fbcce3SJohannes Berg .band = NL80211_BAND_2GHZ, \ 368e84c258SEugene Krasnikov .center_freq = (_freq), \ 378e84c258SEugene Krasnikov .hw_value = (_idx), \ 388e84c258SEugene Krasnikov .max_power = 25, \ 398e84c258SEugene Krasnikov } 408e84c258SEugene Krasnikov 418e84c258SEugene Krasnikov #define CHAN5G(_freq, _idx) { \ 4257fbcce3SJohannes Berg .band = NL80211_BAND_5GHZ, \ 438e84c258SEugene Krasnikov .center_freq = (_freq), \ 448e84c258SEugene Krasnikov .hw_value = (_idx), \ 458e84c258SEugene Krasnikov .max_power = 25, \ 468e84c258SEugene Krasnikov } 478e84c258SEugene Krasnikov 488e84c258SEugene Krasnikov /* The wcn firmware expects channel values to matching 498e84c258SEugene Krasnikov * their mnemonic values. So use these for .hw_value. */ 508e84c258SEugene Krasnikov static struct ieee80211_channel wcn_2ghz_channels[] = { 518e84c258SEugene Krasnikov CHAN2G(2412, 1), /* Channel 1 */ 528e84c258SEugene Krasnikov CHAN2G(2417, 2), /* Channel 2 */ 538e84c258SEugene Krasnikov CHAN2G(2422, 3), /* Channel 3 */ 548e84c258SEugene Krasnikov CHAN2G(2427, 4), /* Channel 4 */ 558e84c258SEugene Krasnikov CHAN2G(2432, 5), /* Channel 5 */ 568e84c258SEugene Krasnikov CHAN2G(2437, 6), /* Channel 6 */ 578e84c258SEugene Krasnikov CHAN2G(2442, 7), /* Channel 7 */ 588e84c258SEugene Krasnikov CHAN2G(2447, 8), /* Channel 8 */ 598e84c258SEugene Krasnikov CHAN2G(2452, 9), /* Channel 9 */ 608e84c258SEugene Krasnikov CHAN2G(2457, 10), /* Channel 10 */ 618e84c258SEugene Krasnikov CHAN2G(2462, 11), /* Channel 11 */ 628e84c258SEugene Krasnikov CHAN2G(2467, 12), /* Channel 12 */ 638e84c258SEugene Krasnikov CHAN2G(2472, 13), /* Channel 13 */ 648e84c258SEugene Krasnikov CHAN2G(2484, 14) /* Channel 14 */ 658e84c258SEugene Krasnikov 668e84c258SEugene Krasnikov }; 678e84c258SEugene Krasnikov 688e84c258SEugene Krasnikov static struct ieee80211_channel wcn_5ghz_channels[] = { 698e84c258SEugene Krasnikov CHAN5G(5180, 36), 708e84c258SEugene Krasnikov CHAN5G(5200, 40), 718e84c258SEugene Krasnikov CHAN5G(5220, 44), 728e84c258SEugene Krasnikov CHAN5G(5240, 48), 738e84c258SEugene Krasnikov CHAN5G(5260, 52), 748e84c258SEugene Krasnikov CHAN5G(5280, 56), 758e84c258SEugene Krasnikov CHAN5G(5300, 60), 768e84c258SEugene Krasnikov CHAN5G(5320, 64), 778e84c258SEugene Krasnikov CHAN5G(5500, 100), 788e84c258SEugene Krasnikov CHAN5G(5520, 104), 798e84c258SEugene Krasnikov CHAN5G(5540, 108), 808e84c258SEugene Krasnikov CHAN5G(5560, 112), 818e84c258SEugene Krasnikov CHAN5G(5580, 116), 828e84c258SEugene Krasnikov CHAN5G(5600, 120), 838e84c258SEugene Krasnikov CHAN5G(5620, 124), 848e84c258SEugene Krasnikov CHAN5G(5640, 128), 858e84c258SEugene Krasnikov CHAN5G(5660, 132), 868e84c258SEugene Krasnikov CHAN5G(5700, 140), 878e84c258SEugene Krasnikov CHAN5G(5745, 149), 888e84c258SEugene Krasnikov CHAN5G(5765, 153), 898e84c258SEugene Krasnikov CHAN5G(5785, 157), 908e84c258SEugene Krasnikov CHAN5G(5805, 161), 918e84c258SEugene Krasnikov CHAN5G(5825, 165) 928e84c258SEugene Krasnikov }; 938e84c258SEugene Krasnikov 948e84c258SEugene Krasnikov #define RATE(_bitrate, _hw_rate, _flags) { \ 958e84c258SEugene Krasnikov .bitrate = (_bitrate), \ 968e84c258SEugene Krasnikov .flags = (_flags), \ 978e84c258SEugene Krasnikov .hw_value = (_hw_rate), \ 988e84c258SEugene Krasnikov .hw_value_short = (_hw_rate) \ 998e84c258SEugene Krasnikov } 1008e84c258SEugene Krasnikov 1018e84c258SEugene Krasnikov static struct ieee80211_rate wcn_2ghz_rates[] = { 1028e84c258SEugene Krasnikov RATE(10, HW_RATE_INDEX_1MBPS, 0), 1038e84c258SEugene Krasnikov RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE), 1048e84c258SEugene Krasnikov RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE), 1058e84c258SEugene Krasnikov RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE), 1068e84c258SEugene Krasnikov RATE(60, HW_RATE_INDEX_6MBPS, 0), 1078e84c258SEugene Krasnikov RATE(90, HW_RATE_INDEX_9MBPS, 0), 1088e84c258SEugene Krasnikov RATE(120, HW_RATE_INDEX_12MBPS, 0), 1098e84c258SEugene Krasnikov RATE(180, HW_RATE_INDEX_18MBPS, 0), 1108e84c258SEugene Krasnikov RATE(240, HW_RATE_INDEX_24MBPS, 0), 1118e84c258SEugene Krasnikov RATE(360, HW_RATE_INDEX_36MBPS, 0), 1128e84c258SEugene Krasnikov RATE(480, HW_RATE_INDEX_48MBPS, 0), 1138e84c258SEugene Krasnikov RATE(540, HW_RATE_INDEX_54MBPS, 0) 1148e84c258SEugene Krasnikov }; 1158e84c258SEugene Krasnikov 1168e84c258SEugene Krasnikov static struct ieee80211_rate wcn_5ghz_rates[] = { 1178e84c258SEugene Krasnikov RATE(60, HW_RATE_INDEX_6MBPS, 0), 1188e84c258SEugene Krasnikov RATE(90, HW_RATE_INDEX_9MBPS, 0), 1198e84c258SEugene Krasnikov RATE(120, HW_RATE_INDEX_12MBPS, 0), 1208e84c258SEugene Krasnikov RATE(180, HW_RATE_INDEX_18MBPS, 0), 1218e84c258SEugene Krasnikov RATE(240, HW_RATE_INDEX_24MBPS, 0), 1228e84c258SEugene Krasnikov RATE(360, HW_RATE_INDEX_36MBPS, 0), 1238e84c258SEugene Krasnikov RATE(480, HW_RATE_INDEX_48MBPS, 0), 1248e84c258SEugene Krasnikov RATE(540, HW_RATE_INDEX_54MBPS, 0) 1258e84c258SEugene Krasnikov }; 1268e84c258SEugene Krasnikov 1278e84c258SEugene Krasnikov static struct ieee80211_supported_band wcn_band_2ghz = { 1288e84c258SEugene Krasnikov .channels = wcn_2ghz_channels, 1298e84c258SEugene Krasnikov .n_channels = ARRAY_SIZE(wcn_2ghz_channels), 1308e84c258SEugene Krasnikov .bitrates = wcn_2ghz_rates, 1318e84c258SEugene Krasnikov .n_bitrates = ARRAY_SIZE(wcn_2ghz_rates), 1328e84c258SEugene Krasnikov .ht_cap = { 1338e84c258SEugene Krasnikov .cap = IEEE80211_HT_CAP_GRN_FLD | 1348e84c258SEugene Krasnikov IEEE80211_HT_CAP_SGI_20 | 1358e84c258SEugene Krasnikov IEEE80211_HT_CAP_DSSSCCK40 | 1368e84c258SEugene Krasnikov IEEE80211_HT_CAP_LSIG_TXOP_PROT, 1378e84c258SEugene Krasnikov .ht_supported = true, 1388e84c258SEugene Krasnikov .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, 1398e84c258SEugene Krasnikov .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, 1408e84c258SEugene Krasnikov .mcs = { 1418e84c258SEugene Krasnikov .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 1428e84c258SEugene Krasnikov .rx_highest = cpu_to_le16(72), 1438e84c258SEugene Krasnikov .tx_params = IEEE80211_HT_MCS_TX_DEFINED, 1448e84c258SEugene Krasnikov } 1458e84c258SEugene Krasnikov } 1468e84c258SEugene Krasnikov }; 1478e84c258SEugene Krasnikov 1488e84c258SEugene Krasnikov static struct ieee80211_supported_band wcn_band_5ghz = { 1498e84c258SEugene Krasnikov .channels = wcn_5ghz_channels, 1508e84c258SEugene Krasnikov .n_channels = ARRAY_SIZE(wcn_5ghz_channels), 1518e84c258SEugene Krasnikov .bitrates = wcn_5ghz_rates, 1528e84c258SEugene Krasnikov .n_bitrates = ARRAY_SIZE(wcn_5ghz_rates), 1538e84c258SEugene Krasnikov .ht_cap = { 1548e84c258SEugene Krasnikov .cap = IEEE80211_HT_CAP_GRN_FLD | 1558e84c258SEugene Krasnikov IEEE80211_HT_CAP_SGI_20 | 1568e84c258SEugene Krasnikov IEEE80211_HT_CAP_DSSSCCK40 | 1578e84c258SEugene Krasnikov IEEE80211_HT_CAP_LSIG_TXOP_PROT | 1588e84c258SEugene Krasnikov IEEE80211_HT_CAP_SGI_40 | 1598e84c258SEugene Krasnikov IEEE80211_HT_CAP_SUP_WIDTH_20_40, 1608e84c258SEugene Krasnikov .ht_supported = true, 1618e84c258SEugene Krasnikov .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, 1628e84c258SEugene Krasnikov .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, 1638e84c258SEugene Krasnikov .mcs = { 1648e84c258SEugene Krasnikov .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 1658e84c258SEugene Krasnikov .rx_highest = cpu_to_le16(72), 1668e84c258SEugene Krasnikov .tx_params = IEEE80211_HT_MCS_TX_DEFINED, 1678e84c258SEugene Krasnikov } 1688e84c258SEugene Krasnikov } 1698e84c258SEugene Krasnikov }; 1708e84c258SEugene Krasnikov 1718e84c258SEugene Krasnikov #ifdef CONFIG_PM 1728e84c258SEugene Krasnikov 1738e84c258SEugene Krasnikov static const struct wiphy_wowlan_support wowlan_support = { 1748e84c258SEugene Krasnikov .flags = WIPHY_WOWLAN_ANY 1758e84c258SEugene Krasnikov }; 1768e84c258SEugene Krasnikov 1778e84c258SEugene Krasnikov #endif 1788e84c258SEugene Krasnikov 1798e84c258SEugene Krasnikov static inline u8 get_sta_index(struct ieee80211_vif *vif, 1808e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv) 1818e84c258SEugene Krasnikov { 1828e84c258SEugene Krasnikov return NL80211_IFTYPE_STATION == vif->type ? 1838e84c258SEugene Krasnikov sta_priv->bss_sta_index : 1848e84c258SEugene Krasnikov sta_priv->sta_index; 1858e84c258SEugene Krasnikov } 1868e84c258SEugene Krasnikov 1872be6636aSPontus Fuchs static const char * const wcn36xx_caps_names[] = { 1882be6636aSPontus Fuchs "MCC", /* 0 */ 1892be6636aSPontus Fuchs "P2P", /* 1 */ 1902be6636aSPontus Fuchs "DOT11AC", /* 2 */ 1912be6636aSPontus Fuchs "SLM_SESSIONIZATION", /* 3 */ 1922be6636aSPontus Fuchs "DOT11AC_OPMODE", /* 4 */ 1932be6636aSPontus Fuchs "SAP32STA", /* 5 */ 1942be6636aSPontus Fuchs "TDLS", /* 6 */ 1952be6636aSPontus Fuchs "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */ 1962be6636aSPontus Fuchs "WLANACTIVE_OFFLOAD", /* 8 */ 1972be6636aSPontus Fuchs "BEACON_OFFLOAD", /* 9 */ 1982be6636aSPontus Fuchs "SCAN_OFFLOAD", /* 10 */ 1992be6636aSPontus Fuchs "ROAM_OFFLOAD", /* 11 */ 2002be6636aSPontus Fuchs "BCN_MISS_OFFLOAD", /* 12 */ 2012be6636aSPontus Fuchs "STA_POWERSAVE", /* 13 */ 2022be6636aSPontus Fuchs "STA_ADVANCED_PWRSAVE", /* 14 */ 2032be6636aSPontus Fuchs "AP_UAPSD", /* 15 */ 2042be6636aSPontus Fuchs "AP_DFS", /* 16 */ 2052be6636aSPontus Fuchs "BLOCKACK", /* 17 */ 2062be6636aSPontus Fuchs "PHY_ERR", /* 18 */ 2072be6636aSPontus Fuchs "BCN_FILTER", /* 19 */ 2082be6636aSPontus Fuchs "RTT", /* 20 */ 2092be6636aSPontus Fuchs "RATECTRL", /* 21 */ 210ffc03c33SBjorn Andersson "WOW", /* 22 */ 211ffc03c33SBjorn Andersson "WLAN_ROAM_SCAN_OFFLOAD", /* 23 */ 212ffc03c33SBjorn Andersson "SPECULATIVE_PS_POLL", /* 24 */ 213ffc03c33SBjorn Andersson "SCAN_SCH", /* 25 */ 214ffc03c33SBjorn Andersson "IBSS_HEARTBEAT_OFFLOAD", /* 26 */ 215ffc03c33SBjorn Andersson "WLAN_SCAN_OFFLOAD", /* 27 */ 216ffc03c33SBjorn Andersson "WLAN_PERIODIC_TX_PTRN", /* 28 */ 217ffc03c33SBjorn Andersson "ADVANCE_TDLS", /* 29 */ 218ffc03c33SBjorn Andersson "BATCH_SCAN", /* 30 */ 219ffc03c33SBjorn Andersson "FW_IN_TX_PATH", /* 31 */ 220ffc03c33SBjorn Andersson "EXTENDED_NSOFFLOAD_SLOT", /* 32 */ 221ffc03c33SBjorn Andersson "CH_SWITCH_V1", /* 33 */ 222ffc03c33SBjorn Andersson "HT40_OBSS_SCAN", /* 34 */ 223ffc03c33SBjorn Andersson "UPDATE_CHANNEL_LIST", /* 35 */ 224ffc03c33SBjorn Andersson "WLAN_MCADDR_FLT", /* 36 */ 225ffc03c33SBjorn Andersson "WLAN_CH144", /* 37 */ 226ffc03c33SBjorn Andersson "NAN", /* 38 */ 227ffc03c33SBjorn Andersson "TDLS_SCAN_COEXISTENCE", /* 39 */ 228ffc03c33SBjorn Andersson "LINK_LAYER_STATS_MEAS", /* 40 */ 229ffc03c33SBjorn Andersson "MU_MIMO", /* 41 */ 230ffc03c33SBjorn Andersson "EXTENDED_SCAN", /* 42 */ 231ffc03c33SBjorn Andersson "DYNAMIC_WMM_PS", /* 43 */ 232ffc03c33SBjorn Andersson "MAC_SPOOFED_SCAN", /* 44 */ 233ffc03c33SBjorn Andersson "BMU_ERROR_GENERIC_RECOVERY", /* 45 */ 234ffc03c33SBjorn Andersson "DISA", /* 46 */ 235ffc03c33SBjorn Andersson "FW_STATS", /* 47 */ 236ffc03c33SBjorn Andersson "WPS_PRBRSP_TMPL", /* 48 */ 237ffc03c33SBjorn Andersson "BCN_IE_FLT_DELTA", /* 49 */ 238ffc03c33SBjorn Andersson "TDLS_OFF_CHANNEL", /* 51 */ 239ffc03c33SBjorn Andersson "RTT3", /* 52 */ 240ffc03c33SBjorn Andersson "MGMT_FRAME_LOGGING", /* 53 */ 241ffc03c33SBjorn Andersson "ENHANCED_TXBD_COMPLETION", /* 54 */ 242ffc03c33SBjorn Andersson "LOGGING_ENHANCEMENT", /* 55 */ 243ffc03c33SBjorn Andersson "EXT_SCAN_ENHANCED", /* 56 */ 244ffc03c33SBjorn Andersson "MEMORY_DUMP_SUPPORTED", /* 57 */ 245ffc03c33SBjorn Andersson "PER_PKT_STATS_SUPPORTED", /* 58 */ 246ffc03c33SBjorn Andersson "EXT_LL_STAT", /* 60 */ 247ffc03c33SBjorn Andersson "WIFI_CONFIG", /* 61 */ 248ffc03c33SBjorn Andersson "ANTENNA_DIVERSITY_SELECTION", /* 62 */ 2492be6636aSPontus Fuchs }; 2502be6636aSPontus Fuchs 2512be6636aSPontus Fuchs static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x) 2522be6636aSPontus Fuchs { 2532be6636aSPontus Fuchs if (x >= ARRAY_SIZE(wcn36xx_caps_names)) 2542be6636aSPontus Fuchs return "UNKNOWN"; 2552be6636aSPontus Fuchs return wcn36xx_caps_names[x]; 2562be6636aSPontus Fuchs } 2572be6636aSPontus Fuchs 2582be6636aSPontus Fuchs static void wcn36xx_feat_caps_info(struct wcn36xx *wcn) 2592be6636aSPontus Fuchs { 2602be6636aSPontus Fuchs int i; 2612be6636aSPontus Fuchs 2622be6636aSPontus Fuchs for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) { 2632be6636aSPontus Fuchs if (get_feat_caps(wcn->fw_feat_caps, i)) 2642be6636aSPontus Fuchs wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i)); 2652be6636aSPontus Fuchs } 2662be6636aSPontus Fuchs } 2672be6636aSPontus Fuchs 2688e84c258SEugene Krasnikov static int wcn36xx_start(struct ieee80211_hw *hw) 2698e84c258SEugene Krasnikov { 2708e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 2718e84c258SEugene Krasnikov int ret; 2728e84c258SEugene Krasnikov 2738e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n"); 2748e84c258SEugene Krasnikov 2758e84c258SEugene Krasnikov /* SMD initialization */ 2768e84c258SEugene Krasnikov ret = wcn36xx_smd_open(wcn); 2778e84c258SEugene Krasnikov if (ret) { 2788e84c258SEugene Krasnikov wcn36xx_err("Failed to open smd channel: %d\n", ret); 2798e84c258SEugene Krasnikov goto out_err; 2808e84c258SEugene Krasnikov } 2818e84c258SEugene Krasnikov 2828e84c258SEugene Krasnikov /* Allocate memory pools for Mgmt BD headers and Data BD headers */ 2838e84c258SEugene Krasnikov ret = wcn36xx_dxe_allocate_mem_pools(wcn); 2848e84c258SEugene Krasnikov if (ret) { 2858e84c258SEugene Krasnikov wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret); 2868e84c258SEugene Krasnikov goto out_smd_close; 2878e84c258SEugene Krasnikov } 2888e84c258SEugene Krasnikov 2898e84c258SEugene Krasnikov ret = wcn36xx_dxe_alloc_ctl_blks(wcn); 2908e84c258SEugene Krasnikov if (ret) { 2918e84c258SEugene Krasnikov wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret); 2928e84c258SEugene Krasnikov goto out_free_dxe_pool; 2938e84c258SEugene Krasnikov } 2948e84c258SEugene Krasnikov 2958e84c258SEugene Krasnikov wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL); 2968e84c258SEugene Krasnikov if (!wcn->hal_buf) { 2978e84c258SEugene Krasnikov wcn36xx_err("Failed to allocate smd buf\n"); 2988e84c258SEugene Krasnikov ret = -ENOMEM; 2998e84c258SEugene Krasnikov goto out_free_dxe_ctl; 3008e84c258SEugene Krasnikov } 3018e84c258SEugene Krasnikov 3028e84c258SEugene Krasnikov ret = wcn36xx_smd_load_nv(wcn); 3038e84c258SEugene Krasnikov if (ret) { 3048e84c258SEugene Krasnikov wcn36xx_err("Failed to push NV to chip\n"); 3058e84c258SEugene Krasnikov goto out_free_smd_buf; 3068e84c258SEugene Krasnikov } 3078e84c258SEugene Krasnikov 3088e84c258SEugene Krasnikov ret = wcn36xx_smd_start(wcn); 3098e84c258SEugene Krasnikov if (ret) { 3108e84c258SEugene Krasnikov wcn36xx_err("Failed to start chip\n"); 3118e84c258SEugene Krasnikov goto out_free_smd_buf; 3128e84c258SEugene Krasnikov } 3138e84c258SEugene Krasnikov 314f2ed5d24SPontus Fuchs if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { 315f2ed5d24SPontus Fuchs ret = wcn36xx_smd_feature_caps_exchange(wcn); 316f2ed5d24SPontus Fuchs if (ret) 317f2ed5d24SPontus Fuchs wcn36xx_warn("Exchange feature caps failed\n"); 318f2ed5d24SPontus Fuchs else 319f2ed5d24SPontus Fuchs wcn36xx_feat_caps_info(wcn); 320f2ed5d24SPontus Fuchs } 321f2ed5d24SPontus Fuchs 3228e84c258SEugene Krasnikov /* DMA channel initialization */ 3238e84c258SEugene Krasnikov ret = wcn36xx_dxe_init(wcn); 3248e84c258SEugene Krasnikov if (ret) { 3258e84c258SEugene Krasnikov wcn36xx_err("DXE init failed\n"); 3268e84c258SEugene Krasnikov goto out_smd_stop; 3278e84c258SEugene Krasnikov } 3288e84c258SEugene Krasnikov 3298e84c258SEugene Krasnikov wcn36xx_debugfs_init(wcn); 3308e84c258SEugene Krasnikov 3318e84c258SEugene Krasnikov INIT_LIST_HEAD(&wcn->vif_list); 3324b12462aSBob Copeland spin_lock_init(&wcn->dxe_lock); 3334b12462aSBob Copeland 3348e84c258SEugene Krasnikov return 0; 3358e84c258SEugene Krasnikov 3368e84c258SEugene Krasnikov out_smd_stop: 3378e84c258SEugene Krasnikov wcn36xx_smd_stop(wcn); 3388e84c258SEugene Krasnikov out_free_smd_buf: 3398e84c258SEugene Krasnikov kfree(wcn->hal_buf); 3408e84c258SEugene Krasnikov out_free_dxe_ctl: 3418e84c258SEugene Krasnikov wcn36xx_dxe_free_ctl_blks(wcn); 3424aa2d31fSChristophe Jaillet out_free_dxe_pool: 3434aa2d31fSChristophe Jaillet wcn36xx_dxe_free_mem_pools(wcn); 3448e84c258SEugene Krasnikov out_smd_close: 3458e84c258SEugene Krasnikov wcn36xx_smd_close(wcn); 3468e84c258SEugene Krasnikov out_err: 3478e84c258SEugene Krasnikov return ret; 3488e84c258SEugene Krasnikov } 3498e84c258SEugene Krasnikov 3508e84c258SEugene Krasnikov static void wcn36xx_stop(struct ieee80211_hw *hw) 3518e84c258SEugene Krasnikov { 3528e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 3538e84c258SEugene Krasnikov 3548e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); 3558e84c258SEugene Krasnikov 3568e84c258SEugene Krasnikov wcn36xx_debugfs_exit(wcn); 3578e84c258SEugene Krasnikov wcn36xx_smd_stop(wcn); 3588e84c258SEugene Krasnikov wcn36xx_dxe_deinit(wcn); 3598e84c258SEugene Krasnikov wcn36xx_smd_close(wcn); 3608e84c258SEugene Krasnikov 3618e84c258SEugene Krasnikov wcn36xx_dxe_free_mem_pools(wcn); 3628e84c258SEugene Krasnikov wcn36xx_dxe_free_ctl_blks(wcn); 3638e84c258SEugene Krasnikov 3648e84c258SEugene Krasnikov kfree(wcn->hal_buf); 3658e84c258SEugene Krasnikov } 3668e84c258SEugene Krasnikov 3678e84c258SEugene Krasnikov static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) 3688e84c258SEugene Krasnikov { 3698e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 3708e84c258SEugene Krasnikov struct ieee80211_vif *vif = NULL; 3718e84c258SEugene Krasnikov struct wcn36xx_vif *tmp; 3728e84c258SEugene Krasnikov 3738e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); 3748e84c258SEugene Krasnikov 37539efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 37639efc7ccSBjorn Andersson 3778e84c258SEugene Krasnikov if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { 3788e84c258SEugene Krasnikov int ch = WCN36XX_HW_CHANNEL(wcn); 3798e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", 3808e84c258SEugene Krasnikov ch); 3818e84c258SEugene Krasnikov list_for_each_entry(tmp, &wcn->vif_list, list) { 382ce75877fSPontus Fuchs vif = wcn36xx_priv_to_vif(tmp); 3838e84c258SEugene Krasnikov wcn36xx_smd_switch_channel(wcn, vif, ch); 3848e84c258SEugene Krasnikov } 3858e84c258SEugene Krasnikov } 3868e84c258SEugene Krasnikov 3870856655aSLoic Poulain if (changed & IEEE80211_CONF_CHANGE_PS) { 3880856655aSLoic Poulain list_for_each_entry(tmp, &wcn->vif_list, list) { 3890856655aSLoic Poulain vif = wcn36xx_priv_to_vif(tmp); 3900856655aSLoic Poulain if (hw->conf.flags & IEEE80211_CONF_PS) { 3910856655aSLoic Poulain if (vif->bss_conf.ps) /* ps allowed ? */ 3920856655aSLoic Poulain wcn36xx_pmc_enter_bmps_state(wcn, vif); 3930856655aSLoic Poulain } else { 3940856655aSLoic Poulain wcn36xx_pmc_exit_bmps_state(wcn, vif); 3950856655aSLoic Poulain } 3960856655aSLoic Poulain } 3970856655aSLoic Poulain } 3980856655aSLoic Poulain 39939efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 40039efc7ccSBjorn Andersson 4018e84c258SEugene Krasnikov return 0; 4028e84c258SEugene Krasnikov } 4038e84c258SEugene Krasnikov 4048e84c258SEugene Krasnikov static void wcn36xx_configure_filter(struct ieee80211_hw *hw, 4058e84c258SEugene Krasnikov unsigned int changed, 4068e84c258SEugene Krasnikov unsigned int *total, u64 multicast) 4078e84c258SEugene Krasnikov { 40820a779edSPontus Fuchs struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp; 40920a779edSPontus Fuchs struct wcn36xx *wcn = hw->priv; 41020a779edSPontus Fuchs struct wcn36xx_vif *tmp; 41120a779edSPontus Fuchs struct ieee80211_vif *vif = NULL; 41220a779edSPontus Fuchs 4138e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); 4148e84c258SEugene Krasnikov 41539efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 41639efc7ccSBjorn Andersson 41720a779edSPontus Fuchs *total &= FIF_ALLMULTI; 41820a779edSPontus Fuchs 41920a779edSPontus Fuchs fp = (void *)(unsigned long)multicast; 42020a779edSPontus Fuchs list_for_each_entry(tmp, &wcn->vif_list, list) { 42120a779edSPontus Fuchs vif = wcn36xx_priv_to_vif(tmp); 42220a779edSPontus Fuchs 42320a779edSPontus Fuchs /* FW handles MC filtering only when connected as STA */ 42420a779edSPontus Fuchs if (*total & FIF_ALLMULTI) 42520a779edSPontus Fuchs wcn36xx_smd_set_mc_list(wcn, vif, NULL); 42620a779edSPontus Fuchs else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc) 42720a779edSPontus Fuchs wcn36xx_smd_set_mc_list(wcn, vif, fp); 42820a779edSPontus Fuchs } 42939efc7ccSBjorn Andersson 43039efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 43120a779edSPontus Fuchs kfree(fp); 43220a779edSPontus Fuchs } 43320a779edSPontus Fuchs 43420a779edSPontus Fuchs static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw, 43520a779edSPontus Fuchs struct netdev_hw_addr_list *mc_list) 43620a779edSPontus Fuchs { 43720a779edSPontus Fuchs struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp; 43820a779edSPontus Fuchs struct netdev_hw_addr *ha; 43920a779edSPontus Fuchs 44020a779edSPontus Fuchs wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n"); 44120a779edSPontus Fuchs fp = kzalloc(sizeof(*fp), GFP_ATOMIC); 44220a779edSPontus Fuchs if (!fp) { 44320a779edSPontus Fuchs wcn36xx_err("Out of memory setting filters.\n"); 44420a779edSPontus Fuchs return 0; 44520a779edSPontus Fuchs } 44620a779edSPontus Fuchs 44720a779edSPontus Fuchs fp->mc_addr_count = 0; 44820a779edSPontus Fuchs /* update multicast filtering parameters */ 44920a779edSPontus Fuchs if (netdev_hw_addr_list_count(mc_list) <= 45020a779edSPontus Fuchs WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) { 45120a779edSPontus Fuchs netdev_hw_addr_list_for_each(ha, mc_list) { 45220a779edSPontus Fuchs memcpy(fp->mc_addr[fp->mc_addr_count], 45320a779edSPontus Fuchs ha->addr, ETH_ALEN); 45420a779edSPontus Fuchs fp->mc_addr_count++; 45520a779edSPontus Fuchs } 45620a779edSPontus Fuchs } 45720a779edSPontus Fuchs 45820a779edSPontus Fuchs return (u64)(unsigned long)fp; 4598e84c258SEugene Krasnikov } 4608e84c258SEugene Krasnikov 4618e84c258SEugene Krasnikov static void wcn36xx_tx(struct ieee80211_hw *hw, 4628e84c258SEugene Krasnikov struct ieee80211_tx_control *control, 4638e84c258SEugene Krasnikov struct sk_buff *skb) 4648e84c258SEugene Krasnikov { 4658e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 4668e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = NULL; 4678e84c258SEugene Krasnikov 4688e84c258SEugene Krasnikov if (control->sta) 469a92e4696SPontus Fuchs sta_priv = wcn36xx_sta_to_priv(control->sta); 4708e84c258SEugene Krasnikov 4718e84c258SEugene Krasnikov if (wcn36xx_start_tx(wcn, sta_priv, skb)) 4728e84c258SEugene Krasnikov ieee80211_free_txskb(wcn->hw, skb); 4738e84c258SEugene Krasnikov } 4748e84c258SEugene Krasnikov 4758e84c258SEugene Krasnikov static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 4768e84c258SEugene Krasnikov struct ieee80211_vif *vif, 4778e84c258SEugene Krasnikov struct ieee80211_sta *sta, 4788e84c258SEugene Krasnikov struct ieee80211_key_conf *key_conf) 4798e84c258SEugene Krasnikov { 4808e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 481ce75877fSPontus Fuchs struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); 48281c69263SPontus Fuchs struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); 4838e84c258SEugene Krasnikov int ret = 0; 4848e84c258SEugene Krasnikov u8 key[WLAN_MAX_KEY_LEN]; 4858e84c258SEugene Krasnikov 4868e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n"); 4878e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n", 4888e84c258SEugene Krasnikov cmd, key_conf->cipher, key_conf->keyidx, 4898e84c258SEugene Krasnikov key_conf->keylen, key_conf->flags); 4908e84c258SEugene Krasnikov wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ", 4918e84c258SEugene Krasnikov key_conf->key, 4928e84c258SEugene Krasnikov key_conf->keylen); 4938e84c258SEugene Krasnikov 49439efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 49539efc7ccSBjorn Andersson 4968e84c258SEugene Krasnikov switch (key_conf->cipher) { 4978e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_WEP40: 4988e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; 4998e84c258SEugene Krasnikov break; 5008e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_WEP104: 5018e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; 5028e84c258SEugene Krasnikov break; 5038e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_CCMP: 5048e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; 5058e84c258SEugene Krasnikov break; 5068e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_TKIP: 5078e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; 5088e84c258SEugene Krasnikov break; 5098e84c258SEugene Krasnikov default: 5108e84c258SEugene Krasnikov wcn36xx_err("Unsupported key type 0x%x\n", 5118e84c258SEugene Krasnikov key_conf->cipher); 5128e84c258SEugene Krasnikov ret = -EOPNOTSUPP; 5138e84c258SEugene Krasnikov goto out; 5148e84c258SEugene Krasnikov } 5158e84c258SEugene Krasnikov 5168e84c258SEugene Krasnikov switch (cmd) { 5178e84c258SEugene Krasnikov case SET_KEY: 5188e84c258SEugene Krasnikov if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { 5198e84c258SEugene Krasnikov /* 5208e84c258SEugene Krasnikov * Supplicant is sending key in the wrong order: 5218e84c258SEugene Krasnikov * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) 5228e84c258SEugene Krasnikov * but HW expects it to be in the order as described in 5238e84c258SEugene Krasnikov * IEEE 802.11 spec (see chapter 11.7) like this: 5248e84c258SEugene Krasnikov * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) 5258e84c258SEugene Krasnikov */ 5268e84c258SEugene Krasnikov memcpy(key, key_conf->key, 16); 5278e84c258SEugene Krasnikov memcpy(key + 16, key_conf->key + 24, 8); 5288e84c258SEugene Krasnikov memcpy(key + 24, key_conf->key + 16, 8); 5298e84c258SEugene Krasnikov } else { 5308e84c258SEugene Krasnikov memcpy(key, key_conf->key, key_conf->keylen); 5318e84c258SEugene Krasnikov } 5328e84c258SEugene Krasnikov 5338e84c258SEugene Krasnikov if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { 5348e84c258SEugene Krasnikov sta_priv->is_data_encrypted = true; 5358e84c258SEugene Krasnikov /* Reconfigure bss with encrypt_type */ 5368e84c258SEugene Krasnikov if (NL80211_IFTYPE_STATION == vif->type) 5378e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, 5388e84c258SEugene Krasnikov vif, 5398e84c258SEugene Krasnikov sta, 5408e84c258SEugene Krasnikov sta->addr, 5418e84c258SEugene Krasnikov true); 5428e84c258SEugene Krasnikov 5438e84c258SEugene Krasnikov wcn36xx_smd_set_stakey(wcn, 5448e84c258SEugene Krasnikov vif_priv->encrypt_type, 5458e84c258SEugene Krasnikov key_conf->keyidx, 5468e84c258SEugene Krasnikov key_conf->keylen, 5478e84c258SEugene Krasnikov key, 5488e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 5498e84c258SEugene Krasnikov } else { 5508e84c258SEugene Krasnikov wcn36xx_smd_set_bsskey(wcn, 5518e84c258SEugene Krasnikov vif_priv->encrypt_type, 5528e84c258SEugene Krasnikov key_conf->keyidx, 5538e84c258SEugene Krasnikov key_conf->keylen, 5548e84c258SEugene Krasnikov key); 5558e84c258SEugene Krasnikov if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) || 5568e84c258SEugene Krasnikov (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) { 5578e84c258SEugene Krasnikov sta_priv->is_data_encrypted = true; 5588e84c258SEugene Krasnikov wcn36xx_smd_set_stakey(wcn, 5598e84c258SEugene Krasnikov vif_priv->encrypt_type, 5608e84c258SEugene Krasnikov key_conf->keyidx, 5618e84c258SEugene Krasnikov key_conf->keylen, 5628e84c258SEugene Krasnikov key, 5638e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 5648e84c258SEugene Krasnikov } 5658e84c258SEugene Krasnikov } 5668e84c258SEugene Krasnikov break; 5678e84c258SEugene Krasnikov case DISABLE_KEY: 5688e84c258SEugene Krasnikov if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { 5692716a8acSPontus Fuchs vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE; 5708e84c258SEugene Krasnikov wcn36xx_smd_remove_bsskey(wcn, 5718e84c258SEugene Krasnikov vif_priv->encrypt_type, 5728e84c258SEugene Krasnikov key_conf->keyidx); 5738e84c258SEugene Krasnikov } else { 5748e84c258SEugene Krasnikov sta_priv->is_data_encrypted = false; 5758e84c258SEugene Krasnikov /* do not remove key if disassociated */ 5768e84c258SEugene Krasnikov if (sta_priv->aid) 5778e84c258SEugene Krasnikov wcn36xx_smd_remove_stakey(wcn, 5788e84c258SEugene Krasnikov vif_priv->encrypt_type, 5798e84c258SEugene Krasnikov key_conf->keyidx, 5808e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 5818e84c258SEugene Krasnikov } 5828e84c258SEugene Krasnikov break; 5838e84c258SEugene Krasnikov default: 5848e84c258SEugene Krasnikov wcn36xx_err("Unsupported key cmd 0x%x\n", cmd); 5858e84c258SEugene Krasnikov ret = -EOPNOTSUPP; 5868e84c258SEugene Krasnikov goto out; 5878e84c258SEugene Krasnikov } 5888e84c258SEugene Krasnikov 5898e84c258SEugene Krasnikov out: 59039efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 59139efc7ccSBjorn Andersson 5928e84c258SEugene Krasnikov return ret; 5938e84c258SEugene Krasnikov } 5948e84c258SEugene Krasnikov 59588603903SBjorn Andersson static void wcn36xx_hw_scan_worker(struct work_struct *work) 5968e84c258SEugene Krasnikov { 59788603903SBjorn Andersson struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work); 59888603903SBjorn Andersson struct cfg80211_scan_request *req = wcn->scan_req; 59988603903SBjorn Andersson u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; 60088603903SBjorn Andersson struct cfg80211_scan_info scan_info = {}; 60103c95dbeSBjorn Andersson bool aborted = false; 60288603903SBjorn Andersson int i; 60388603903SBjorn Andersson 60488603903SBjorn Andersson wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels); 60588603903SBjorn Andersson 60688603903SBjorn Andersson for (i = 0; i < req->n_channels; i++) 60788603903SBjorn Andersson channels[i] = req->channels[i]->hw_value; 60888603903SBjorn Andersson 60988603903SBjorn Andersson wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels); 6108e84c258SEugene Krasnikov 6118e84c258SEugene Krasnikov wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); 61288603903SBjorn Andersson for (i = 0; i < req->n_channels; i++) { 61303c95dbeSBjorn Andersson mutex_lock(&wcn->scan_lock); 61403c95dbeSBjorn Andersson aborted = wcn->scan_aborted; 61503c95dbeSBjorn Andersson mutex_unlock(&wcn->scan_lock); 61603c95dbeSBjorn Andersson 61703c95dbeSBjorn Andersson if (aborted) 61803c95dbeSBjorn Andersson break; 61903c95dbeSBjorn Andersson 62088603903SBjorn Andersson wcn->scan_freq = req->channels[i]->center_freq; 62188603903SBjorn Andersson wcn->scan_band = req->channels[i]->band; 62288603903SBjorn Andersson 62388603903SBjorn Andersson wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value); 62488603903SBjorn Andersson msleep(30); 62588603903SBjorn Andersson wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value); 62688603903SBjorn Andersson 62788603903SBjorn Andersson wcn->scan_freq = 0; 62888603903SBjorn Andersson } 62988603903SBjorn Andersson wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); 63088603903SBjorn Andersson 63103c95dbeSBjorn Andersson scan_info.aborted = aborted; 63288603903SBjorn Andersson ieee80211_scan_completed(wcn->hw, &scan_info); 63388603903SBjorn Andersson 63488603903SBjorn Andersson mutex_lock(&wcn->scan_lock); 63588603903SBjorn Andersson wcn->scan_req = NULL; 63688603903SBjorn Andersson mutex_unlock(&wcn->scan_lock); 6378e84c258SEugene Krasnikov } 6388e84c258SEugene Krasnikov 63988603903SBjorn Andersson static int wcn36xx_hw_scan(struct ieee80211_hw *hw, 64088603903SBjorn Andersson struct ieee80211_vif *vif, 64188603903SBjorn Andersson struct ieee80211_scan_request *hw_req) 6428e84c258SEugene Krasnikov { 6438e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 64488603903SBjorn Andersson mutex_lock(&wcn->scan_lock); 64588603903SBjorn Andersson if (wcn->scan_req) { 64688603903SBjorn Andersson mutex_unlock(&wcn->scan_lock); 64788603903SBjorn Andersson return -EBUSY; 64888603903SBjorn Andersson } 64903c95dbeSBjorn Andersson 65003c95dbeSBjorn Andersson wcn->scan_aborted = false; 65188603903SBjorn Andersson wcn->scan_req = &hw_req->req; 6522f3bef4bSLoic Poulain 65388603903SBjorn Andersson mutex_unlock(&wcn->scan_lock); 65488603903SBjorn Andersson 6552f3bef4bSLoic Poulain if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { 6562f3bef4bSLoic Poulain /* legacy manual/sw scan */ 65788603903SBjorn Andersson schedule_work(&wcn->scan_work); 65888603903SBjorn Andersson return 0; 6598e84c258SEugene Krasnikov } 6608e84c258SEugene Krasnikov 6612f3bef4bSLoic Poulain return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req); 6622f3bef4bSLoic Poulain } 6632f3bef4bSLoic Poulain 66403c95dbeSBjorn Andersson static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw, 66503c95dbeSBjorn Andersson struct ieee80211_vif *vif) 66603c95dbeSBjorn Andersson { 66703c95dbeSBjorn Andersson struct wcn36xx *wcn = hw->priv; 66803c95dbeSBjorn Andersson 66903c95dbeSBjorn Andersson mutex_lock(&wcn->scan_lock); 67003c95dbeSBjorn Andersson wcn->scan_aborted = true; 67103c95dbeSBjorn Andersson mutex_unlock(&wcn->scan_lock); 67203c95dbeSBjorn Andersson 6739bfd05e3SLoic Poulain /* ieee80211_scan_completed will be called on FW scan indication */ 6749bfd05e3SLoic Poulain wcn36xx_smd_stop_hw_scan(wcn); 6759bfd05e3SLoic Poulain 67603c95dbeSBjorn Andersson cancel_work_sync(&wcn->scan_work); 67703c95dbeSBjorn Andersson } 67803c95dbeSBjorn Andersson 6798e84c258SEugene Krasnikov static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, 68057fbcce3SJohannes Berg enum nl80211_band band) 6818e84c258SEugene Krasnikov { 6828e84c258SEugene Krasnikov int i, size; 6838e84c258SEugene Krasnikov u16 *rates_table; 684a92e4696SPontus Fuchs struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); 6858e84c258SEugene Krasnikov u32 rates = sta->supp_rates[band]; 6868e84c258SEugene Krasnikov 6878e84c258SEugene Krasnikov memset(&sta_priv->supported_rates, 0, 6888e84c258SEugene Krasnikov sizeof(sta_priv->supported_rates)); 6898e84c258SEugene Krasnikov sta_priv->supported_rates.op_rate_mode = STA_11n; 6908e84c258SEugene Krasnikov 6918e84c258SEugene Krasnikov size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); 6928e84c258SEugene Krasnikov rates_table = sta_priv->supported_rates.dsss_rates; 69357fbcce3SJohannes Berg if (band == NL80211_BAND_2GHZ) { 6948e84c258SEugene Krasnikov for (i = 0; i < size; i++) { 6958e84c258SEugene Krasnikov if (rates & 0x01) { 6968e84c258SEugene Krasnikov rates_table[i] = wcn_2ghz_rates[i].hw_value; 6978e84c258SEugene Krasnikov rates = rates >> 1; 6988e84c258SEugene Krasnikov } 6998e84c258SEugene Krasnikov } 7008e84c258SEugene Krasnikov } 7018e84c258SEugene Krasnikov 7028e84c258SEugene Krasnikov size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates); 7038e84c258SEugene Krasnikov rates_table = sta_priv->supported_rates.ofdm_rates; 7048e84c258SEugene Krasnikov for (i = 0; i < size; i++) { 7058e84c258SEugene Krasnikov if (rates & 0x01) { 7068e84c258SEugene Krasnikov rates_table[i] = wcn_5ghz_rates[i].hw_value; 7078e84c258SEugene Krasnikov rates = rates >> 1; 7088e84c258SEugene Krasnikov } 7098e84c258SEugene Krasnikov } 7108e84c258SEugene Krasnikov 7118e84c258SEugene Krasnikov if (sta->ht_cap.ht_supported) { 7128e84c258SEugene Krasnikov BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) > 7138e84c258SEugene Krasnikov sizeof(sta_priv->supported_rates.supported_mcs_set)); 7148e84c258SEugene Krasnikov memcpy(sta_priv->supported_rates.supported_mcs_set, 7158e84c258SEugene Krasnikov sta->ht_cap.mcs.rx_mask, 7168e84c258SEugene Krasnikov sizeof(sta->ht_cap.mcs.rx_mask)); 7178e84c258SEugene Krasnikov } 7188e84c258SEugene Krasnikov } 7198e84c258SEugene Krasnikov void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) 7208e84c258SEugene Krasnikov { 7218e84c258SEugene Krasnikov u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { 7228e84c258SEugene Krasnikov HW_RATE_INDEX_6MBPS, 7238e84c258SEugene Krasnikov HW_RATE_INDEX_9MBPS, 7248e84c258SEugene Krasnikov HW_RATE_INDEX_12MBPS, 7258e84c258SEugene Krasnikov HW_RATE_INDEX_18MBPS, 7268e84c258SEugene Krasnikov HW_RATE_INDEX_24MBPS, 7278e84c258SEugene Krasnikov HW_RATE_INDEX_36MBPS, 7288e84c258SEugene Krasnikov HW_RATE_INDEX_48MBPS, 7298e84c258SEugene Krasnikov HW_RATE_INDEX_54MBPS 7308e84c258SEugene Krasnikov }; 7318e84c258SEugene Krasnikov u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = { 7328e84c258SEugene Krasnikov HW_RATE_INDEX_1MBPS, 7338e84c258SEugene Krasnikov HW_RATE_INDEX_2MBPS, 7348e84c258SEugene Krasnikov HW_RATE_INDEX_5_5MBPS, 7358e84c258SEugene Krasnikov HW_RATE_INDEX_11MBPS 7368e84c258SEugene Krasnikov }; 7378e84c258SEugene Krasnikov 7388e84c258SEugene Krasnikov rates->op_rate_mode = STA_11n; 7398e84c258SEugene Krasnikov memcpy(rates->dsss_rates, dsss_rates, 7408e84c258SEugene Krasnikov sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES); 7418e84c258SEugene Krasnikov memcpy(rates->ofdm_rates, ofdm_rates, 7428e84c258SEugene Krasnikov sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); 7438e84c258SEugene Krasnikov rates->supported_mcs_set[0] = 0xFF; 7448e84c258SEugene Krasnikov } 7458e84c258SEugene Krasnikov static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, 7468e84c258SEugene Krasnikov struct ieee80211_vif *vif, 7478e84c258SEugene Krasnikov struct ieee80211_bss_conf *bss_conf, 7488e84c258SEugene Krasnikov u32 changed) 7498e84c258SEugene Krasnikov { 7508e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 7518e84c258SEugene Krasnikov struct sk_buff *skb = NULL; 7528e84c258SEugene Krasnikov u16 tim_off, tim_len; 7538e84c258SEugene Krasnikov enum wcn36xx_hal_link_state link_state; 754ce75877fSPontus Fuchs struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); 7558e84c258SEugene Krasnikov 7568e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n", 7578e84c258SEugene Krasnikov vif, changed); 7588e84c258SEugene Krasnikov 75939efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 76039efc7ccSBjorn Andersson 7618e84c258SEugene Krasnikov if (changed & BSS_CHANGED_BEACON_INFO) { 7628e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 7638e84c258SEugene Krasnikov "mac bss changed dtim period %d\n", 7648e84c258SEugene Krasnikov bss_conf->dtim_period); 7658e84c258SEugene Krasnikov 7668e84c258SEugene Krasnikov vif_priv->dtim_period = bss_conf->dtim_period; 7678e84c258SEugene Krasnikov } 7688e84c258SEugene Krasnikov 7698e84c258SEugene Krasnikov if (changed & BSS_CHANGED_BSSID) { 7708e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n", 7718e84c258SEugene Krasnikov bss_conf->bssid); 7728e84c258SEugene Krasnikov 7738e84c258SEugene Krasnikov if (!is_zero_ether_addr(bss_conf->bssid)) { 7748e84c258SEugene Krasnikov vif_priv->is_joining = true; 77590023c03SPontus Fuchs vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; 7768e84c258SEugene Krasnikov wcn36xx_smd_join(wcn, bss_conf->bssid, 7778e84c258SEugene Krasnikov vif->addr, WCN36XX_HW_CHANNEL(wcn)); 7788e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, vif, NULL, 7798e84c258SEugene Krasnikov bss_conf->bssid, false); 7808e84c258SEugene Krasnikov } else { 7818e84c258SEugene Krasnikov vif_priv->is_joining = false; 7828e84c258SEugene Krasnikov wcn36xx_smd_delete_bss(wcn, vif); 7832716a8acSPontus Fuchs vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE; 7848e84c258SEugene Krasnikov } 7858e84c258SEugene Krasnikov } 7868e84c258SEugene Krasnikov 7878e84c258SEugene Krasnikov if (changed & BSS_CHANGED_SSID) { 7888e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 7898e84c258SEugene Krasnikov "mac bss changed ssid\n"); 7908e84c258SEugene Krasnikov wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ", 7918e84c258SEugene Krasnikov bss_conf->ssid, bss_conf->ssid_len); 7928e84c258SEugene Krasnikov 7938e84c258SEugene Krasnikov vif_priv->ssid.length = bss_conf->ssid_len; 7948e84c258SEugene Krasnikov memcpy(&vif_priv->ssid.ssid, 7958e84c258SEugene Krasnikov bss_conf->ssid, 7968e84c258SEugene Krasnikov bss_conf->ssid_len); 7978e84c258SEugene Krasnikov } 7988e84c258SEugene Krasnikov 7998e84c258SEugene Krasnikov if (changed & BSS_CHANGED_ASSOC) { 8008e84c258SEugene Krasnikov vif_priv->is_joining = false; 8018e84c258SEugene Krasnikov if (bss_conf->assoc) { 8028e84c258SEugene Krasnikov struct ieee80211_sta *sta; 8038e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv; 8048e84c258SEugene Krasnikov 8058e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 8068e84c258SEugene Krasnikov "mac assoc bss %pM vif %pM AID=%d\n", 8078e84c258SEugene Krasnikov bss_conf->bssid, 8088e84c258SEugene Krasnikov vif->addr, 8098e84c258SEugene Krasnikov bss_conf->aid); 8108e84c258SEugene Krasnikov 811043ce546SPontus Fuchs vif_priv->sta_assoc = true; 81239efc7ccSBjorn Andersson 81339efc7ccSBjorn Andersson /* 81439efc7ccSBjorn Andersson * Holding conf_mutex ensures mutal exclusion with 81539efc7ccSBjorn Andersson * wcn36xx_sta_remove() and as such ensures that sta 81639efc7ccSBjorn Andersson * won't be freed while we're operating on it. As such 81739efc7ccSBjorn Andersson * we do not need to hold the rcu_read_lock(). 81839efc7ccSBjorn Andersson */ 8198e84c258SEugene Krasnikov sta = ieee80211_find_sta(vif, bss_conf->bssid); 8208e84c258SEugene Krasnikov if (!sta) { 8218e84c258SEugene Krasnikov wcn36xx_err("sta %pM is not found\n", 8228e84c258SEugene Krasnikov bss_conf->bssid); 8238e84c258SEugene Krasnikov goto out; 8248e84c258SEugene Krasnikov } 825a92e4696SPontus Fuchs sta_priv = wcn36xx_sta_to_priv(sta); 8268e84c258SEugene Krasnikov 8278e84c258SEugene Krasnikov wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); 8288e84c258SEugene Krasnikov 8298e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, 8308e84c258SEugene Krasnikov vif->addr, 8318e84c258SEugene Krasnikov WCN36XX_HAL_LINK_POSTASSOC_STATE); 8328e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, vif, sta, 8338e84c258SEugene Krasnikov bss_conf->bssid, 8348e84c258SEugene Krasnikov true); 8358e84c258SEugene Krasnikov sta_priv->aid = bss_conf->aid; 8368e84c258SEugene Krasnikov /* 8378e84c258SEugene Krasnikov * config_sta must be called from because this is the 8388e84c258SEugene Krasnikov * place where AID is available. 8398e84c258SEugene Krasnikov */ 8408e84c258SEugene Krasnikov wcn36xx_smd_config_sta(wcn, vif, sta); 8418e84c258SEugene Krasnikov } else { 8428e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 8438e84c258SEugene Krasnikov "disassociated bss %pM vif %pM AID=%d\n", 8448e84c258SEugene Krasnikov bss_conf->bssid, 8458e84c258SEugene Krasnikov vif->addr, 8468e84c258SEugene Krasnikov bss_conf->aid); 847043ce546SPontus Fuchs vif_priv->sta_assoc = false; 8488e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, 8498e84c258SEugene Krasnikov bss_conf->bssid, 8508e84c258SEugene Krasnikov vif->addr, 8518e84c258SEugene Krasnikov WCN36XX_HAL_LINK_IDLE_STATE); 8528e84c258SEugene Krasnikov } 8538e84c258SEugene Krasnikov } 8548e84c258SEugene Krasnikov 8558e84c258SEugene Krasnikov if (changed & BSS_CHANGED_AP_PROBE_RESP) { 8568e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n"); 8578e84c258SEugene Krasnikov skb = ieee80211_proberesp_get(hw, vif); 8588e84c258SEugene Krasnikov if (!skb) { 8598e84c258SEugene Krasnikov wcn36xx_err("failed to alloc probereq skb\n"); 8608e84c258SEugene Krasnikov goto out; 8618e84c258SEugene Krasnikov } 8628e84c258SEugene Krasnikov 8638e84c258SEugene Krasnikov wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb); 8648e84c258SEugene Krasnikov dev_kfree_skb(skb); 8658e84c258SEugene Krasnikov } 8668e84c258SEugene Krasnikov 867b3e3f871SChun-Yeow Yeoh if (changed & BSS_CHANGED_BEACON_ENABLED || 868b3e3f871SChun-Yeow Yeoh changed & BSS_CHANGED_BEACON) { 8698e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 8708e84c258SEugene Krasnikov "mac bss changed beacon enabled %d\n", 8718e84c258SEugene Krasnikov bss_conf->enable_beacon); 8728e84c258SEugene Krasnikov 8738e84c258SEugene Krasnikov if (bss_conf->enable_beacon) { 874908628dbSPontus Fuchs vif_priv->dtim_period = bss_conf->dtim_period; 87590023c03SPontus Fuchs vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; 8768e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, vif, NULL, 8778e84c258SEugene Krasnikov vif->addr, false); 8788e84c258SEugene Krasnikov skb = ieee80211_beacon_get_tim(hw, vif, &tim_off, 8798e84c258SEugene Krasnikov &tim_len); 8808e84c258SEugene Krasnikov if (!skb) { 8818e84c258SEugene Krasnikov wcn36xx_err("failed to alloc beacon skb\n"); 8828e84c258SEugene Krasnikov goto out; 8838e84c258SEugene Krasnikov } 8848e84c258SEugene Krasnikov wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0); 8858e84c258SEugene Krasnikov dev_kfree_skb(skb); 8868e84c258SEugene Krasnikov 8878e84c258SEugene Krasnikov if (vif->type == NL80211_IFTYPE_ADHOC || 8888e84c258SEugene Krasnikov vif->type == NL80211_IFTYPE_MESH_POINT) 8898e84c258SEugene Krasnikov link_state = WCN36XX_HAL_LINK_IBSS_STATE; 8908e84c258SEugene Krasnikov else 8918e84c258SEugene Krasnikov link_state = WCN36XX_HAL_LINK_AP_STATE; 8928e84c258SEugene Krasnikov 8938e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, 8948e84c258SEugene Krasnikov link_state); 8958e84c258SEugene Krasnikov } else { 8965443918dSBjorn Andersson wcn36xx_smd_delete_bss(wcn, vif); 8978e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, 8988e84c258SEugene Krasnikov WCN36XX_HAL_LINK_IDLE_STATE); 8998e84c258SEugene Krasnikov } 9008e84c258SEugene Krasnikov } 9018e84c258SEugene Krasnikov out: 90239efc7ccSBjorn Andersson 90339efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 90439efc7ccSBjorn Andersson 9058e84c258SEugene Krasnikov return; 9068e84c258SEugene Krasnikov } 9078e84c258SEugene Krasnikov 9088e84c258SEugene Krasnikov /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ 9098e84c258SEugene Krasnikov static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) 9108e84c258SEugene Krasnikov { 9118e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 9128e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value); 9138e84c258SEugene Krasnikov 91439efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 9158e84c258SEugene Krasnikov wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); 91639efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 91739efc7ccSBjorn Andersson 9188e84c258SEugene Krasnikov return 0; 9198e84c258SEugene Krasnikov } 9208e84c258SEugene Krasnikov 9218e84c258SEugene Krasnikov static void wcn36xx_remove_interface(struct ieee80211_hw *hw, 9228e84c258SEugene Krasnikov struct ieee80211_vif *vif) 9238e84c258SEugene Krasnikov { 9248e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 925ce75877fSPontus Fuchs struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); 9268e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif); 9278e84c258SEugene Krasnikov 92839efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 92939efc7ccSBjorn Andersson 9308e84c258SEugene Krasnikov list_del(&vif_priv->list); 9318e84c258SEugene Krasnikov wcn36xx_smd_delete_sta_self(wcn, vif->addr); 93239efc7ccSBjorn Andersson 93339efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 9348e84c258SEugene Krasnikov } 9358e84c258SEugene Krasnikov 9368e84c258SEugene Krasnikov static int wcn36xx_add_interface(struct ieee80211_hw *hw, 9378e84c258SEugene Krasnikov struct ieee80211_vif *vif) 9388e84c258SEugene Krasnikov { 9398e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 940ce75877fSPontus Fuchs struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); 9418e84c258SEugene Krasnikov 9428e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n", 9438e84c258SEugene Krasnikov vif, vif->type); 9448e84c258SEugene Krasnikov 9458e84c258SEugene Krasnikov if (!(NL80211_IFTYPE_STATION == vif->type || 9468e84c258SEugene Krasnikov NL80211_IFTYPE_AP == vif->type || 9478e84c258SEugene Krasnikov NL80211_IFTYPE_ADHOC == vif->type || 9488e84c258SEugene Krasnikov NL80211_IFTYPE_MESH_POINT == vif->type)) { 9498e84c258SEugene Krasnikov wcn36xx_warn("Unsupported interface type requested: %d\n", 9508e84c258SEugene Krasnikov vif->type); 9518e84c258SEugene Krasnikov return -EOPNOTSUPP; 9528e84c258SEugene Krasnikov } 9538e84c258SEugene Krasnikov 95439efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 95539efc7ccSBjorn Andersson 9568e84c258SEugene Krasnikov list_add(&vif_priv->list, &wcn->vif_list); 9578e84c258SEugene Krasnikov wcn36xx_smd_add_sta_self(wcn, vif); 9588e84c258SEugene Krasnikov 95939efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 96039efc7ccSBjorn Andersson 9618e84c258SEugene Krasnikov return 0; 9628e84c258SEugene Krasnikov } 9638e84c258SEugene Krasnikov 9648e84c258SEugene Krasnikov static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 9658e84c258SEugene Krasnikov struct ieee80211_sta *sta) 9668e84c258SEugene Krasnikov { 9678e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 968ce75877fSPontus Fuchs struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); 969a92e4696SPontus Fuchs struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); 9708e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", 9718e84c258SEugene Krasnikov vif, sta->addr); 9728e84c258SEugene Krasnikov 97339efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 97439efc7ccSBjorn Andersson 975e26dc173SBob Copeland spin_lock_init(&sta_priv->ampdu_lock); 9768e84c258SEugene Krasnikov sta_priv->vif = vif_priv; 9778e84c258SEugene Krasnikov /* 9788e84c258SEugene Krasnikov * For STA mode HW will be configured on BSS_CHANGED_ASSOC because 9798e84c258SEugene Krasnikov * at this stage AID is not available yet. 9808e84c258SEugene Krasnikov */ 9818e84c258SEugene Krasnikov if (NL80211_IFTYPE_STATION != vif->type) { 9828e84c258SEugene Krasnikov wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); 9838e84c258SEugene Krasnikov sta_priv->aid = sta->aid; 9848e84c258SEugene Krasnikov wcn36xx_smd_config_sta(wcn, vif, sta); 9858e84c258SEugene Krasnikov } 98639efc7ccSBjorn Andersson 98739efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 98839efc7ccSBjorn Andersson 9898e84c258SEugene Krasnikov return 0; 9908e84c258SEugene Krasnikov } 9918e84c258SEugene Krasnikov 9928e84c258SEugene Krasnikov static int wcn36xx_sta_remove(struct ieee80211_hw *hw, 9938e84c258SEugene Krasnikov struct ieee80211_vif *vif, 9948e84c258SEugene Krasnikov struct ieee80211_sta *sta) 9958e84c258SEugene Krasnikov { 9968e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 997a92e4696SPontus Fuchs struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); 9988e84c258SEugene Krasnikov 9998e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n", 10008e84c258SEugene Krasnikov vif, sta->addr, sta_priv->sta_index); 10018e84c258SEugene Krasnikov 100239efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 100339efc7ccSBjorn Andersson 10048e84c258SEugene Krasnikov wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index); 10058e84c258SEugene Krasnikov sta_priv->vif = NULL; 100639efc7ccSBjorn Andersson 100739efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 100839efc7ccSBjorn Andersson 10098e84c258SEugene Krasnikov return 0; 10108e84c258SEugene Krasnikov } 10118e84c258SEugene Krasnikov 10128e84c258SEugene Krasnikov #ifdef CONFIG_PM 10138e84c258SEugene Krasnikov 10148e84c258SEugene Krasnikov static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) 10158e84c258SEugene Krasnikov { 10168e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 10178e84c258SEugene Krasnikov 10188e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n"); 10198e84c258SEugene Krasnikov 10208e84c258SEugene Krasnikov flush_workqueue(wcn->hal_ind_wq); 10218e84c258SEugene Krasnikov wcn36xx_smd_set_power_params(wcn, true); 10228e84c258SEugene Krasnikov return 0; 10238e84c258SEugene Krasnikov } 10248e84c258SEugene Krasnikov 10258e84c258SEugene Krasnikov static int wcn36xx_resume(struct ieee80211_hw *hw) 10268e84c258SEugene Krasnikov { 10278e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 10288e84c258SEugene Krasnikov 10298e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n"); 10308e84c258SEugene Krasnikov 10318e84c258SEugene Krasnikov flush_workqueue(wcn->hal_ind_wq); 10328e84c258SEugene Krasnikov wcn36xx_smd_set_power_params(wcn, false); 10338e84c258SEugene Krasnikov return 0; 10348e84c258SEugene Krasnikov } 10358e84c258SEugene Krasnikov 10368e84c258SEugene Krasnikov #endif 10378e84c258SEugene Krasnikov 10388e84c258SEugene Krasnikov static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, 10398e84c258SEugene Krasnikov struct ieee80211_vif *vif, 104050ea05efSSara Sharon struct ieee80211_ampdu_params *params) 10418e84c258SEugene Krasnikov { 10428e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 1043a92e4696SPontus Fuchs struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta); 104450ea05efSSara Sharon struct ieee80211_sta *sta = params->sta; 104550ea05efSSara Sharon enum ieee80211_ampdu_mlme_action action = params->action; 104650ea05efSSara Sharon u16 tid = params->tid; 104750ea05efSSara Sharon u16 *ssn = ¶ms->ssn; 10488e84c258SEugene Krasnikov 10498e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", 10508e84c258SEugene Krasnikov action, tid); 10518e84c258SEugene Krasnikov 105239efc7ccSBjorn Andersson mutex_lock(&wcn->conf_mutex); 105339efc7ccSBjorn Andersson 10548e84c258SEugene Krasnikov switch (action) { 10558e84c258SEugene Krasnikov case IEEE80211_AMPDU_RX_START: 10568e84c258SEugene Krasnikov sta_priv->tid = tid; 10578e84c258SEugene Krasnikov wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, 10588e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 10598e84c258SEugene Krasnikov wcn36xx_smd_add_ba(wcn); 10608e84c258SEugene Krasnikov wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); 10618e84c258SEugene Krasnikov break; 10628e84c258SEugene Krasnikov case IEEE80211_AMPDU_RX_STOP: 10638e84c258SEugene Krasnikov wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); 10648e84c258SEugene Krasnikov break; 10658e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_START: 1066e26dc173SBob Copeland spin_lock_bh(&sta_priv->ampdu_lock); 1067e26dc173SBob Copeland sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START; 1068e26dc173SBob Copeland spin_unlock_bh(&sta_priv->ampdu_lock); 1069e26dc173SBob Copeland 10708e84c258SEugene Krasnikov ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); 10718e84c258SEugene Krasnikov break; 10728e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_OPERATIONAL: 1073e26dc173SBob Copeland spin_lock_bh(&sta_priv->ampdu_lock); 1074e26dc173SBob Copeland sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL; 1075e26dc173SBob Copeland spin_unlock_bh(&sta_priv->ampdu_lock); 1076e26dc173SBob Copeland 10778e84c258SEugene Krasnikov wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, 10788e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 10798e84c258SEugene Krasnikov break; 10808e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_STOP_FLUSH: 10818e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: 10828e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_STOP_CONT: 1083e26dc173SBob Copeland spin_lock_bh(&sta_priv->ampdu_lock); 1084e26dc173SBob Copeland sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE; 1085e26dc173SBob Copeland spin_unlock_bh(&sta_priv->ampdu_lock); 1086e26dc173SBob Copeland 10878e84c258SEugene Krasnikov ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 10888e84c258SEugene Krasnikov break; 10898e84c258SEugene Krasnikov default: 10908e84c258SEugene Krasnikov wcn36xx_err("Unknown AMPDU action\n"); 10918e84c258SEugene Krasnikov } 10928e84c258SEugene Krasnikov 109339efc7ccSBjorn Andersson mutex_unlock(&wcn->conf_mutex); 109439efc7ccSBjorn Andersson 10958e84c258SEugene Krasnikov return 0; 10968e84c258SEugene Krasnikov } 10978e84c258SEugene Krasnikov 10988e84c258SEugene Krasnikov static const struct ieee80211_ops wcn36xx_ops = { 10998e84c258SEugene Krasnikov .start = wcn36xx_start, 11008e84c258SEugene Krasnikov .stop = wcn36xx_stop, 11018e84c258SEugene Krasnikov .add_interface = wcn36xx_add_interface, 11028e84c258SEugene Krasnikov .remove_interface = wcn36xx_remove_interface, 11038e84c258SEugene Krasnikov #ifdef CONFIG_PM 11048e84c258SEugene Krasnikov .suspend = wcn36xx_suspend, 11058e84c258SEugene Krasnikov .resume = wcn36xx_resume, 11068e84c258SEugene Krasnikov #endif 11078e84c258SEugene Krasnikov .config = wcn36xx_config, 110820a779edSPontus Fuchs .prepare_multicast = wcn36xx_prepare_multicast, 11098e84c258SEugene Krasnikov .configure_filter = wcn36xx_configure_filter, 11108e84c258SEugene Krasnikov .tx = wcn36xx_tx, 11118e84c258SEugene Krasnikov .set_key = wcn36xx_set_key, 111288603903SBjorn Andersson .hw_scan = wcn36xx_hw_scan, 111303c95dbeSBjorn Andersson .cancel_hw_scan = wcn36xx_cancel_hw_scan, 11148e84c258SEugene Krasnikov .bss_info_changed = wcn36xx_bss_info_changed, 11158e84c258SEugene Krasnikov .set_rts_threshold = wcn36xx_set_rts_threshold, 11168e84c258SEugene Krasnikov .sta_add = wcn36xx_sta_add, 11178e84c258SEugene Krasnikov .sta_remove = wcn36xx_sta_remove, 11188e84c258SEugene Krasnikov .ampdu_action = wcn36xx_ampdu_action, 11198e84c258SEugene Krasnikov }; 11208e84c258SEugene Krasnikov 11218e84c258SEugene Krasnikov static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) 11228e84c258SEugene Krasnikov { 11238e84c258SEugene Krasnikov int ret = 0; 11248e84c258SEugene Krasnikov 11258e84c258SEugene Krasnikov static const u32 cipher_suites[] = { 11268e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_WEP40, 11278e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_WEP104, 11288e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_TKIP, 11298e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_CCMP, 11308e84c258SEugene Krasnikov }; 11318e84c258SEugene Krasnikov 113230686bf7SJohannes Berg ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY); 113330686bf7SJohannes Berg ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION); 113430686bf7SJohannes Berg ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR); 113530686bf7SJohannes Berg ieee80211_hw_set(wcn->hw, SUPPORTS_PS); 113630686bf7SJohannes Berg ieee80211_hw_set(wcn->hw, SIGNAL_DBM); 113730686bf7SJohannes Berg ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); 113888603903SBjorn Andersson ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); 11398e84c258SEugene Krasnikov 11408e84c258SEugene Krasnikov wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 11418e84c258SEugene Krasnikov BIT(NL80211_IFTYPE_AP) | 11428e84c258SEugene Krasnikov BIT(NL80211_IFTYPE_ADHOC) | 11438e84c258SEugene Krasnikov BIT(NL80211_IFTYPE_MESH_POINT); 11448e84c258SEugene Krasnikov 114557fbcce3SJohannes Berg wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz; 1146fd52bdaeSLoic Poulain if (wcn->rf_id != RF_IRIS_WCN3620) 114757fbcce3SJohannes Berg wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz; 11488e84c258SEugene Krasnikov 114988603903SBjorn Andersson wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS; 115088603903SBjorn Andersson wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN; 115188603903SBjorn Andersson 11528e84c258SEugene Krasnikov wcn->hw->wiphy->cipher_suites = cipher_suites; 11538e84c258SEugene Krasnikov wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); 11548e84c258SEugene Krasnikov 11558e84c258SEugene Krasnikov wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; 11568e84c258SEugene Krasnikov 11578e84c258SEugene Krasnikov #ifdef CONFIG_PM 11588e84c258SEugene Krasnikov wcn->hw->wiphy->wowlan = &wowlan_support; 11598e84c258SEugene Krasnikov #endif 11608e84c258SEugene Krasnikov 11618e84c258SEugene Krasnikov wcn->hw->max_listen_interval = 200; 11628e84c258SEugene Krasnikov 11638e84c258SEugene Krasnikov wcn->hw->queues = 4; 11648e84c258SEugene Krasnikov 11658e84c258SEugene Krasnikov SET_IEEE80211_DEV(wcn->hw, wcn->dev); 11668e84c258SEugene Krasnikov 11678e84c258SEugene Krasnikov wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); 11688e84c258SEugene Krasnikov wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); 11698e84c258SEugene Krasnikov 1170ae44b502SAndrew Zaborowski wiphy_ext_feature_set(wcn->hw->wiphy, 1171ae44b502SAndrew Zaborowski NL80211_EXT_FEATURE_CQM_RSSI_LIST); 1172ae44b502SAndrew Zaborowski 11738e84c258SEugene Krasnikov return ret; 11748e84c258SEugene Krasnikov } 11758e84c258SEugene Krasnikov 11768e84c258SEugene Krasnikov static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, 11778e84c258SEugene Krasnikov struct platform_device *pdev) 11788e84c258SEugene Krasnikov { 117905ddce49SBjorn Andersson struct device_node *mmio_node; 1180fd52bdaeSLoic Poulain struct device_node *iris_node; 11818e84c258SEugene Krasnikov struct resource *res; 118205ddce49SBjorn Andersson int index; 118305ddce49SBjorn Andersson int ret; 118405ddce49SBjorn Andersson 11858e84c258SEugene Krasnikov /* Set TX IRQ */ 1186f303a931SBjorn Andersson res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx"); 11878e84c258SEugene Krasnikov if (!res) { 11888e84c258SEugene Krasnikov wcn36xx_err("failed to get tx_irq\n"); 11898e84c258SEugene Krasnikov return -ENOENT; 11908e84c258SEugene Krasnikov } 11918e84c258SEugene Krasnikov wcn->tx_irq = res->start; 11928e84c258SEugene Krasnikov 11938e84c258SEugene Krasnikov /* Set RX IRQ */ 1194f303a931SBjorn Andersson res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx"); 11958e84c258SEugene Krasnikov if (!res) { 11968e84c258SEugene Krasnikov wcn36xx_err("failed to get rx_irq\n"); 11978e84c258SEugene Krasnikov return -ENOENT; 11988e84c258SEugene Krasnikov } 11998e84c258SEugene Krasnikov wcn->rx_irq = res->start; 12008e84c258SEugene Krasnikov 1201f303a931SBjorn Andersson /* Acquire SMSM tx enable handle */ 1202f303a931SBjorn Andersson wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev, 1203f303a931SBjorn Andersson "tx-enable", &wcn->tx_enable_state_bit); 1204f303a931SBjorn Andersson if (IS_ERR(wcn->tx_enable_state)) { 1205f303a931SBjorn Andersson wcn36xx_err("failed to get tx-enable state\n"); 1206f303a931SBjorn Andersson return PTR_ERR(wcn->tx_enable_state); 1207f303a931SBjorn Andersson } 1208f303a931SBjorn Andersson 1209f303a931SBjorn Andersson /* Acquire SMSM tx rings empty handle */ 1210f303a931SBjorn Andersson wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev, 1211f303a931SBjorn Andersson "tx-rings-empty", &wcn->tx_rings_empty_state_bit); 1212f303a931SBjorn Andersson if (IS_ERR(wcn->tx_rings_empty_state)) { 1213f303a931SBjorn Andersson wcn36xx_err("failed to get tx-rings-empty state\n"); 1214f303a931SBjorn Andersson return PTR_ERR(wcn->tx_rings_empty_state); 1215f303a931SBjorn Andersson } 1216f303a931SBjorn Andersson 121705ddce49SBjorn Andersson mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0); 121805ddce49SBjorn Andersson if (!mmio_node) { 121905ddce49SBjorn Andersson wcn36xx_err("failed to acquire qcom,mmio reference\n"); 122005ddce49SBjorn Andersson return -EINVAL; 12218e84c258SEugene Krasnikov } 122205ddce49SBjorn Andersson 12236f10b4e1SBjorn Andersson wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto"); 12246f10b4e1SBjorn Andersson 122505ddce49SBjorn Andersson /* Map the CCU memory */ 122605ddce49SBjorn Andersson index = of_property_match_string(mmio_node, "reg-names", "ccu"); 122705ddce49SBjorn Andersson wcn->ccu_base = of_iomap(mmio_node, index); 122805ddce49SBjorn Andersson if (!wcn->ccu_base) { 122905ddce49SBjorn Andersson wcn36xx_err("failed to map ccu memory\n"); 123005ddce49SBjorn Andersson ret = -ENOMEM; 123105ddce49SBjorn Andersson goto put_mmio_node; 12328e84c258SEugene Krasnikov } 123305ddce49SBjorn Andersson 123405ddce49SBjorn Andersson /* Map the DXE memory */ 123505ddce49SBjorn Andersson index = of_property_match_string(mmio_node, "reg-names", "dxe"); 123605ddce49SBjorn Andersson wcn->dxe_base = of_iomap(mmio_node, index); 123705ddce49SBjorn Andersson if (!wcn->dxe_base) { 123805ddce49SBjorn Andersson wcn36xx_err("failed to map dxe memory\n"); 123905ddce49SBjorn Andersson ret = -ENOMEM; 124005ddce49SBjorn Andersson goto unmap_ccu; 124105ddce49SBjorn Andersson } 124205ddce49SBjorn Andersson 1243fd52bdaeSLoic Poulain /* External RF module */ 12441967c128SJohan Hovold iris_node = of_get_child_by_name(mmio_node, "iris"); 1245fd52bdaeSLoic Poulain if (iris_node) { 1246fd52bdaeSLoic Poulain if (of_device_is_compatible(iris_node, "qcom,wcn3620")) 1247fd52bdaeSLoic Poulain wcn->rf_id = RF_IRIS_WCN3620; 1248fd52bdaeSLoic Poulain of_node_put(iris_node); 1249fd52bdaeSLoic Poulain } 1250fd52bdaeSLoic Poulain 125105ddce49SBjorn Andersson of_node_put(mmio_node); 12528e84c258SEugene Krasnikov return 0; 125305ddce49SBjorn Andersson 125405ddce49SBjorn Andersson unmap_ccu: 125505ddce49SBjorn Andersson iounmap(wcn->ccu_base); 125605ddce49SBjorn Andersson put_mmio_node: 125705ddce49SBjorn Andersson of_node_put(mmio_node); 125805ddce49SBjorn Andersson return ret; 12598e84c258SEugene Krasnikov } 12608e84c258SEugene Krasnikov 12618e84c258SEugene Krasnikov static int wcn36xx_probe(struct platform_device *pdev) 12628e84c258SEugene Krasnikov { 12638e84c258SEugene Krasnikov struct ieee80211_hw *hw; 12648e84c258SEugene Krasnikov struct wcn36xx *wcn; 1265f303a931SBjorn Andersson void *wcnss; 12668e84c258SEugene Krasnikov int ret; 1267f303a931SBjorn Andersson const u8 *addr; 12688e84c258SEugene Krasnikov 12698e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); 12708e84c258SEugene Krasnikov 1271f303a931SBjorn Andersson wcnss = dev_get_drvdata(pdev->dev.parent); 1272f303a931SBjorn Andersson 12738e84c258SEugene Krasnikov hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); 12748e84c258SEugene Krasnikov if (!hw) { 12758e84c258SEugene Krasnikov wcn36xx_err("failed to alloc hw\n"); 12768e84c258SEugene Krasnikov ret = -ENOMEM; 12778e84c258SEugene Krasnikov goto out_err; 12788e84c258SEugene Krasnikov } 12798e84c258SEugene Krasnikov platform_set_drvdata(pdev, hw); 12808e84c258SEugene Krasnikov wcn = hw->priv; 12818e84c258SEugene Krasnikov wcn->hw = hw; 12828e84c258SEugene Krasnikov wcn->dev = &pdev->dev; 128339efc7ccSBjorn Andersson mutex_init(&wcn->conf_mutex); 12848e84c258SEugene Krasnikov mutex_init(&wcn->hal_mutex); 128588603903SBjorn Andersson mutex_init(&wcn->scan_lock); 128688603903SBjorn Andersson 128788603903SBjorn Andersson INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker); 12888e84c258SEugene Krasnikov 12895052de8dSBjorn Andersson wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw); 1290f303a931SBjorn Andersson if (IS_ERR(wcn->smd_channel)) { 1291f303a931SBjorn Andersson wcn36xx_err("failed to open WLAN_CTRL channel\n"); 1292f303a931SBjorn Andersson ret = PTR_ERR(wcn->smd_channel); 1293f303a931SBjorn Andersson goto out_wq; 1294f303a931SBjorn Andersson } 1295f303a931SBjorn Andersson 1296f303a931SBjorn Andersson addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret); 1297f303a931SBjorn Andersson if (addr && ret != ETH_ALEN) { 1298f303a931SBjorn Andersson wcn36xx_err("invalid local-mac-address\n"); 1299f303a931SBjorn Andersson ret = -EINVAL; 1300f303a931SBjorn Andersson goto out_wq; 1301f303a931SBjorn Andersson } else if (addr) { 13028e84c258SEugene Krasnikov wcn36xx_info("mac address: %pM\n", addr); 13038e84c258SEugene Krasnikov SET_IEEE80211_PERM_ADDR(wcn->hw, addr); 13048e84c258SEugene Krasnikov } 13058e84c258SEugene Krasnikov 13068e84c258SEugene Krasnikov ret = wcn36xx_platform_get_resources(wcn, pdev); 13078e84c258SEugene Krasnikov if (ret) 13088e84c258SEugene Krasnikov goto out_wq; 13098e84c258SEugene Krasnikov 13108e84c258SEugene Krasnikov wcn36xx_init_ieee80211(wcn); 13118e84c258SEugene Krasnikov ret = ieee80211_register_hw(wcn->hw); 13128e84c258SEugene Krasnikov if (ret) 13138e84c258SEugene Krasnikov goto out_unmap; 13148e84c258SEugene Krasnikov 13158e84c258SEugene Krasnikov return 0; 13168e84c258SEugene Krasnikov 13178e84c258SEugene Krasnikov out_unmap: 131805ddce49SBjorn Andersson iounmap(wcn->ccu_base); 131905ddce49SBjorn Andersson iounmap(wcn->dxe_base); 13208e84c258SEugene Krasnikov out_wq: 13218e84c258SEugene Krasnikov ieee80211_free_hw(hw); 13228e84c258SEugene Krasnikov out_err: 13238e84c258SEugene Krasnikov return ret; 13248e84c258SEugene Krasnikov } 1325f303a931SBjorn Andersson 13268e84c258SEugene Krasnikov static int wcn36xx_remove(struct platform_device *pdev) 13278e84c258SEugene Krasnikov { 13288e84c258SEugene Krasnikov struct ieee80211_hw *hw = platform_get_drvdata(pdev); 13298e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 13308e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n"); 13318e84c258SEugene Krasnikov 13324bda7fafSPontus Fuchs release_firmware(wcn->nv); 13338e84c258SEugene Krasnikov 13348e84c258SEugene Krasnikov ieee80211_unregister_hw(hw); 1335f303a931SBjorn Andersson 1336f303a931SBjorn Andersson qcom_smem_state_put(wcn->tx_enable_state); 1337f303a931SBjorn Andersson qcom_smem_state_put(wcn->tx_rings_empty_state); 1338f303a931SBjorn Andersson 1339efad8396SBjorn Andersson rpmsg_destroy_ept(wcn->smd_channel); 1340efad8396SBjorn Andersson 134105ddce49SBjorn Andersson iounmap(wcn->dxe_base); 134205ddce49SBjorn Andersson iounmap(wcn->ccu_base); 1343d5362888SBjorn Andersson 1344d5362888SBjorn Andersson mutex_destroy(&wcn->hal_mutex); 13458e84c258SEugene Krasnikov ieee80211_free_hw(hw); 13468e84c258SEugene Krasnikov 13478e84c258SEugene Krasnikov return 0; 13488e84c258SEugene Krasnikov } 1349f303a931SBjorn Andersson 1350f303a931SBjorn Andersson static const struct of_device_id wcn36xx_of_match[] = { 1351f303a931SBjorn Andersson { .compatible = "qcom,wcnss-wlan" }, 13528e84c258SEugene Krasnikov {} 13538e84c258SEugene Krasnikov }; 1354f303a931SBjorn Andersson MODULE_DEVICE_TABLE(of, wcn36xx_of_match); 13558e84c258SEugene Krasnikov 13568e84c258SEugene Krasnikov static struct platform_driver wcn36xx_driver = { 13578e84c258SEugene Krasnikov .probe = wcn36xx_probe, 13588e84c258SEugene Krasnikov .remove = wcn36xx_remove, 13598e84c258SEugene Krasnikov .driver = { 13608e84c258SEugene Krasnikov .name = "wcn36xx", 1361f303a931SBjorn Andersson .of_match_table = wcn36xx_of_match, 13628e84c258SEugene Krasnikov }, 13638e84c258SEugene Krasnikov }; 13648e84c258SEugene Krasnikov 1365f303a931SBjorn Andersson module_platform_driver(wcn36xx_driver); 13668e84c258SEugene Krasnikov 13678e84c258SEugene Krasnikov MODULE_LICENSE("Dual BSD/GPL"); 13688e84c258SEugene Krasnikov MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); 13698e84c258SEugene Krasnikov MODULE_FIRMWARE(WLAN_NV_FILE); 1370