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 1818e84c258SEugene Krasnikov static int wcn36xx_start(struct ieee80211_hw *hw) 1828e84c258SEugene Krasnikov { 1838e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 1848e84c258SEugene Krasnikov int ret; 1858e84c258SEugene Krasnikov 1868e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n"); 1878e84c258SEugene Krasnikov 1888e84c258SEugene Krasnikov /* SMD initialization */ 1898e84c258SEugene Krasnikov ret = wcn36xx_smd_open(wcn); 1908e84c258SEugene Krasnikov if (ret) { 1918e84c258SEugene Krasnikov wcn36xx_err("Failed to open smd channel: %d\n", ret); 1928e84c258SEugene Krasnikov goto out_err; 1938e84c258SEugene Krasnikov } 1948e84c258SEugene Krasnikov 1958e84c258SEugene Krasnikov /* Allocate memory pools for Mgmt BD headers and Data BD headers */ 1968e84c258SEugene Krasnikov ret = wcn36xx_dxe_allocate_mem_pools(wcn); 1978e84c258SEugene Krasnikov if (ret) { 1988e84c258SEugene Krasnikov wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret); 1998e84c258SEugene Krasnikov goto out_smd_close; 2008e84c258SEugene Krasnikov } 2018e84c258SEugene Krasnikov 2028e84c258SEugene Krasnikov ret = wcn36xx_dxe_alloc_ctl_blks(wcn); 2038e84c258SEugene Krasnikov if (ret) { 2048e84c258SEugene Krasnikov wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret); 2058e84c258SEugene Krasnikov goto out_free_dxe_pool; 2068e84c258SEugene Krasnikov } 2078e84c258SEugene Krasnikov 2088e84c258SEugene Krasnikov wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL); 2098e84c258SEugene Krasnikov if (!wcn->hal_buf) { 2108e84c258SEugene Krasnikov wcn36xx_err("Failed to allocate smd buf\n"); 2118e84c258SEugene Krasnikov ret = -ENOMEM; 2128e84c258SEugene Krasnikov goto out_free_dxe_ctl; 2138e84c258SEugene Krasnikov } 2148e84c258SEugene Krasnikov 2158e84c258SEugene Krasnikov ret = wcn36xx_smd_load_nv(wcn); 2168e84c258SEugene Krasnikov if (ret) { 2178e84c258SEugene Krasnikov wcn36xx_err("Failed to push NV to chip\n"); 2188e84c258SEugene Krasnikov goto out_free_smd_buf; 2198e84c258SEugene Krasnikov } 2208e84c258SEugene Krasnikov 2218e84c258SEugene Krasnikov ret = wcn36xx_smd_start(wcn); 2228e84c258SEugene Krasnikov if (ret) { 2238e84c258SEugene Krasnikov wcn36xx_err("Failed to start chip\n"); 2248e84c258SEugene Krasnikov goto out_free_smd_buf; 2258e84c258SEugene Krasnikov } 2268e84c258SEugene Krasnikov 2278e84c258SEugene Krasnikov /* DMA channel initialization */ 2288e84c258SEugene Krasnikov ret = wcn36xx_dxe_init(wcn); 2298e84c258SEugene Krasnikov if (ret) { 2308e84c258SEugene Krasnikov wcn36xx_err("DXE init failed\n"); 2318e84c258SEugene Krasnikov goto out_smd_stop; 2328e84c258SEugene Krasnikov } 2338e84c258SEugene Krasnikov 2348e84c258SEugene Krasnikov wcn36xx_debugfs_init(wcn); 2358e84c258SEugene Krasnikov 2368e84c258SEugene Krasnikov if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { 2378e84c258SEugene Krasnikov ret = wcn36xx_smd_feature_caps_exchange(wcn); 2388e84c258SEugene Krasnikov if (ret) 2398e84c258SEugene Krasnikov wcn36xx_warn("Exchange feature caps failed\n"); 2408e84c258SEugene Krasnikov } 2418e84c258SEugene Krasnikov INIT_LIST_HEAD(&wcn->vif_list); 2428e84c258SEugene Krasnikov return 0; 2438e84c258SEugene Krasnikov 2448e84c258SEugene Krasnikov out_smd_stop: 2458e84c258SEugene Krasnikov wcn36xx_smd_stop(wcn); 2468e84c258SEugene Krasnikov out_free_smd_buf: 2478e84c258SEugene Krasnikov kfree(wcn->hal_buf); 2488e84c258SEugene Krasnikov out_free_dxe_pool: 2498e84c258SEugene Krasnikov wcn36xx_dxe_free_mem_pools(wcn); 2508e84c258SEugene Krasnikov out_free_dxe_ctl: 2518e84c258SEugene Krasnikov wcn36xx_dxe_free_ctl_blks(wcn); 2528e84c258SEugene Krasnikov out_smd_close: 2538e84c258SEugene Krasnikov wcn36xx_smd_close(wcn); 2548e84c258SEugene Krasnikov out_err: 2558e84c258SEugene Krasnikov return ret; 2568e84c258SEugene Krasnikov } 2578e84c258SEugene Krasnikov 2588e84c258SEugene Krasnikov static void wcn36xx_stop(struct ieee80211_hw *hw) 2598e84c258SEugene Krasnikov { 2608e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 2618e84c258SEugene Krasnikov 2628e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); 2638e84c258SEugene Krasnikov 2648e84c258SEugene Krasnikov wcn36xx_debugfs_exit(wcn); 2658e84c258SEugene Krasnikov wcn36xx_smd_stop(wcn); 2668e84c258SEugene Krasnikov wcn36xx_dxe_deinit(wcn); 2678e84c258SEugene Krasnikov wcn36xx_smd_close(wcn); 2688e84c258SEugene Krasnikov 2698e84c258SEugene Krasnikov wcn36xx_dxe_free_mem_pools(wcn); 2708e84c258SEugene Krasnikov wcn36xx_dxe_free_ctl_blks(wcn); 2718e84c258SEugene Krasnikov 2728e84c258SEugene Krasnikov kfree(wcn->hal_buf); 2738e84c258SEugene Krasnikov } 2748e84c258SEugene Krasnikov 2758e84c258SEugene Krasnikov static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) 2768e84c258SEugene Krasnikov { 2778e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 2788e84c258SEugene Krasnikov struct ieee80211_vif *vif = NULL; 2798e84c258SEugene Krasnikov struct wcn36xx_vif *tmp; 2808e84c258SEugene Krasnikov 2818e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); 2828e84c258SEugene Krasnikov 2838e84c258SEugene Krasnikov if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { 2848e84c258SEugene Krasnikov int ch = WCN36XX_HW_CHANNEL(wcn); 2858e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", 2868e84c258SEugene Krasnikov ch); 2878e84c258SEugene Krasnikov list_for_each_entry(tmp, &wcn->vif_list, list) { 2888e84c258SEugene Krasnikov vif = container_of((void *)tmp, 2898e84c258SEugene Krasnikov struct ieee80211_vif, 2908e84c258SEugene Krasnikov drv_priv); 2918e84c258SEugene Krasnikov wcn36xx_smd_switch_channel(wcn, vif, ch); 2928e84c258SEugene Krasnikov } 2938e84c258SEugene Krasnikov } 2948e84c258SEugene Krasnikov 2958e84c258SEugene Krasnikov return 0; 2968e84c258SEugene Krasnikov } 2978e84c258SEugene Krasnikov 2988e84c258SEugene Krasnikov #define WCN36XX_SUPPORTED_FILTERS (0) 2998e84c258SEugene Krasnikov 3008e84c258SEugene Krasnikov static void wcn36xx_configure_filter(struct ieee80211_hw *hw, 3018e84c258SEugene Krasnikov unsigned int changed, 3028e84c258SEugene Krasnikov unsigned int *total, u64 multicast) 3038e84c258SEugene Krasnikov { 3048e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); 3058e84c258SEugene Krasnikov 3068e84c258SEugene Krasnikov *total &= WCN36XX_SUPPORTED_FILTERS; 3078e84c258SEugene Krasnikov } 3088e84c258SEugene Krasnikov 3098e84c258SEugene Krasnikov static void wcn36xx_tx(struct ieee80211_hw *hw, 3108e84c258SEugene Krasnikov struct ieee80211_tx_control *control, 3118e84c258SEugene Krasnikov struct sk_buff *skb) 3128e84c258SEugene Krasnikov { 3138e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 3148e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = NULL; 3158e84c258SEugene Krasnikov 3168e84c258SEugene Krasnikov if (control->sta) 3178e84c258SEugene Krasnikov sta_priv = (struct wcn36xx_sta *)control->sta->drv_priv; 3188e84c258SEugene Krasnikov 3198e84c258SEugene Krasnikov if (wcn36xx_start_tx(wcn, sta_priv, skb)) 3208e84c258SEugene Krasnikov ieee80211_free_txskb(wcn->hw, skb); 3218e84c258SEugene Krasnikov } 3228e84c258SEugene Krasnikov 3238e84c258SEugene Krasnikov static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 3248e84c258SEugene Krasnikov struct ieee80211_vif *vif, 3258e84c258SEugene Krasnikov struct ieee80211_sta *sta, 3268e84c258SEugene Krasnikov struct ieee80211_key_conf *key_conf) 3278e84c258SEugene Krasnikov { 3288e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 3298e84c258SEugene Krasnikov struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; 3308e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = vif_priv->sta; 3318e84c258SEugene Krasnikov int ret = 0; 3328e84c258SEugene Krasnikov u8 key[WLAN_MAX_KEY_LEN]; 3338e84c258SEugene Krasnikov 3348e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n"); 3358e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n", 3368e84c258SEugene Krasnikov cmd, key_conf->cipher, key_conf->keyidx, 3378e84c258SEugene Krasnikov key_conf->keylen, key_conf->flags); 3388e84c258SEugene Krasnikov wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ", 3398e84c258SEugene Krasnikov key_conf->key, 3408e84c258SEugene Krasnikov key_conf->keylen); 3418e84c258SEugene Krasnikov 3428e84c258SEugene Krasnikov switch (key_conf->cipher) { 3438e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_WEP40: 3448e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; 3458e84c258SEugene Krasnikov break; 3468e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_WEP104: 3478e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; 3488e84c258SEugene Krasnikov break; 3498e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_CCMP: 3508e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; 3518e84c258SEugene Krasnikov break; 3528e84c258SEugene Krasnikov case WLAN_CIPHER_SUITE_TKIP: 3538e84c258SEugene Krasnikov vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; 3548e84c258SEugene Krasnikov break; 3558e84c258SEugene Krasnikov default: 3568e84c258SEugene Krasnikov wcn36xx_err("Unsupported key type 0x%x\n", 3578e84c258SEugene Krasnikov key_conf->cipher); 3588e84c258SEugene Krasnikov ret = -EOPNOTSUPP; 3598e84c258SEugene Krasnikov goto out; 3608e84c258SEugene Krasnikov } 3618e84c258SEugene Krasnikov 3628e84c258SEugene Krasnikov switch (cmd) { 3638e84c258SEugene Krasnikov case SET_KEY: 3648e84c258SEugene Krasnikov if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { 3658e84c258SEugene Krasnikov /* 3668e84c258SEugene Krasnikov * Supplicant is sending key in the wrong order: 3678e84c258SEugene Krasnikov * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) 3688e84c258SEugene Krasnikov * but HW expects it to be in the order as described in 3698e84c258SEugene Krasnikov * IEEE 802.11 spec (see chapter 11.7) like this: 3708e84c258SEugene Krasnikov * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) 3718e84c258SEugene Krasnikov */ 3728e84c258SEugene Krasnikov memcpy(key, key_conf->key, 16); 3738e84c258SEugene Krasnikov memcpy(key + 16, key_conf->key + 24, 8); 3748e84c258SEugene Krasnikov memcpy(key + 24, key_conf->key + 16, 8); 3758e84c258SEugene Krasnikov } else { 3768e84c258SEugene Krasnikov memcpy(key, key_conf->key, key_conf->keylen); 3778e84c258SEugene Krasnikov } 3788e84c258SEugene Krasnikov 3798e84c258SEugene Krasnikov if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { 3808e84c258SEugene Krasnikov sta_priv->is_data_encrypted = true; 3818e84c258SEugene Krasnikov /* Reconfigure bss with encrypt_type */ 3828e84c258SEugene Krasnikov if (NL80211_IFTYPE_STATION == vif->type) 3838e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, 3848e84c258SEugene Krasnikov vif, 3858e84c258SEugene Krasnikov sta, 3868e84c258SEugene Krasnikov sta->addr, 3878e84c258SEugene Krasnikov true); 3888e84c258SEugene Krasnikov 3898e84c258SEugene Krasnikov wcn36xx_smd_set_stakey(wcn, 3908e84c258SEugene Krasnikov vif_priv->encrypt_type, 3918e84c258SEugene Krasnikov key_conf->keyidx, 3928e84c258SEugene Krasnikov key_conf->keylen, 3938e84c258SEugene Krasnikov key, 3948e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 3958e84c258SEugene Krasnikov } else { 3968e84c258SEugene Krasnikov wcn36xx_smd_set_bsskey(wcn, 3978e84c258SEugene Krasnikov vif_priv->encrypt_type, 3988e84c258SEugene Krasnikov key_conf->keyidx, 3998e84c258SEugene Krasnikov key_conf->keylen, 4008e84c258SEugene Krasnikov key); 4018e84c258SEugene Krasnikov if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) || 4028e84c258SEugene Krasnikov (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) { 4038e84c258SEugene Krasnikov sta_priv->is_data_encrypted = true; 4048e84c258SEugene Krasnikov wcn36xx_smd_set_stakey(wcn, 4058e84c258SEugene Krasnikov vif_priv->encrypt_type, 4068e84c258SEugene Krasnikov key_conf->keyidx, 4078e84c258SEugene Krasnikov key_conf->keylen, 4088e84c258SEugene Krasnikov key, 4098e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 4108e84c258SEugene Krasnikov } 4118e84c258SEugene Krasnikov } 4128e84c258SEugene Krasnikov break; 4138e84c258SEugene Krasnikov case DISABLE_KEY: 4148e84c258SEugene Krasnikov if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { 4158e84c258SEugene Krasnikov wcn36xx_smd_remove_bsskey(wcn, 4168e84c258SEugene Krasnikov vif_priv->encrypt_type, 4178e84c258SEugene Krasnikov key_conf->keyidx); 4188e84c258SEugene Krasnikov } else { 4198e84c258SEugene Krasnikov sta_priv->is_data_encrypted = false; 4208e84c258SEugene Krasnikov /* do not remove key if disassociated */ 4218e84c258SEugene Krasnikov if (sta_priv->aid) 4228e84c258SEugene Krasnikov wcn36xx_smd_remove_stakey(wcn, 4238e84c258SEugene Krasnikov vif_priv->encrypt_type, 4248e84c258SEugene Krasnikov key_conf->keyidx, 4258e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 4268e84c258SEugene Krasnikov } 4278e84c258SEugene Krasnikov break; 4288e84c258SEugene Krasnikov default: 4298e84c258SEugene Krasnikov wcn36xx_err("Unsupported key cmd 0x%x\n", cmd); 4308e84c258SEugene Krasnikov ret = -EOPNOTSUPP; 4318e84c258SEugene Krasnikov goto out; 4328e84c258SEugene Krasnikov break; 4338e84c258SEugene Krasnikov } 4348e84c258SEugene Krasnikov 4358e84c258SEugene Krasnikov out: 4368e84c258SEugene Krasnikov return ret; 4378e84c258SEugene Krasnikov } 4388e84c258SEugene Krasnikov 4398e84c258SEugene Krasnikov static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw) 4408e84c258SEugene Krasnikov { 4418e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 4428e84c258SEugene Krasnikov 4438e84c258SEugene Krasnikov wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); 4448e84c258SEugene Krasnikov wcn36xx_smd_start_scan(wcn); 4458e84c258SEugene Krasnikov } 4468e84c258SEugene Krasnikov 4478e84c258SEugene Krasnikov static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw) 4488e84c258SEugene Krasnikov { 4498e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 4508e84c258SEugene Krasnikov 4518e84c258SEugene Krasnikov wcn36xx_smd_end_scan(wcn); 4528e84c258SEugene Krasnikov wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); 4538e84c258SEugene Krasnikov } 4548e84c258SEugene Krasnikov 4558e84c258SEugene Krasnikov static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, 4568e84c258SEugene Krasnikov enum ieee80211_band band) 4578e84c258SEugene Krasnikov { 4588e84c258SEugene Krasnikov int i, size; 4598e84c258SEugene Krasnikov u16 *rates_table; 4608e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; 4618e84c258SEugene Krasnikov u32 rates = sta->supp_rates[band]; 4628e84c258SEugene Krasnikov 4638e84c258SEugene Krasnikov memset(&sta_priv->supported_rates, 0, 4648e84c258SEugene Krasnikov sizeof(sta_priv->supported_rates)); 4658e84c258SEugene Krasnikov sta_priv->supported_rates.op_rate_mode = STA_11n; 4668e84c258SEugene Krasnikov 4678e84c258SEugene Krasnikov size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); 4688e84c258SEugene Krasnikov rates_table = sta_priv->supported_rates.dsss_rates; 4698e84c258SEugene Krasnikov if (band == IEEE80211_BAND_2GHZ) { 4708e84c258SEugene Krasnikov for (i = 0; i < size; i++) { 4718e84c258SEugene Krasnikov if (rates & 0x01) { 4728e84c258SEugene Krasnikov rates_table[i] = wcn_2ghz_rates[i].hw_value; 4738e84c258SEugene Krasnikov rates = rates >> 1; 4748e84c258SEugene Krasnikov } 4758e84c258SEugene Krasnikov } 4768e84c258SEugene Krasnikov } 4778e84c258SEugene Krasnikov 4788e84c258SEugene Krasnikov size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates); 4798e84c258SEugene Krasnikov rates_table = sta_priv->supported_rates.ofdm_rates; 4808e84c258SEugene Krasnikov for (i = 0; i < size; i++) { 4818e84c258SEugene Krasnikov if (rates & 0x01) { 4828e84c258SEugene Krasnikov rates_table[i] = wcn_5ghz_rates[i].hw_value; 4838e84c258SEugene Krasnikov rates = rates >> 1; 4848e84c258SEugene Krasnikov } 4858e84c258SEugene Krasnikov } 4868e84c258SEugene Krasnikov 4878e84c258SEugene Krasnikov if (sta->ht_cap.ht_supported) { 4888e84c258SEugene Krasnikov BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) > 4898e84c258SEugene Krasnikov sizeof(sta_priv->supported_rates.supported_mcs_set)); 4908e84c258SEugene Krasnikov memcpy(sta_priv->supported_rates.supported_mcs_set, 4918e84c258SEugene Krasnikov sta->ht_cap.mcs.rx_mask, 4928e84c258SEugene Krasnikov sizeof(sta->ht_cap.mcs.rx_mask)); 4938e84c258SEugene Krasnikov } 4948e84c258SEugene Krasnikov } 4958e84c258SEugene Krasnikov void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) 4968e84c258SEugene Krasnikov { 4978e84c258SEugene Krasnikov u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { 4988e84c258SEugene Krasnikov HW_RATE_INDEX_6MBPS, 4998e84c258SEugene Krasnikov HW_RATE_INDEX_9MBPS, 5008e84c258SEugene Krasnikov HW_RATE_INDEX_12MBPS, 5018e84c258SEugene Krasnikov HW_RATE_INDEX_18MBPS, 5028e84c258SEugene Krasnikov HW_RATE_INDEX_24MBPS, 5038e84c258SEugene Krasnikov HW_RATE_INDEX_36MBPS, 5048e84c258SEugene Krasnikov HW_RATE_INDEX_48MBPS, 5058e84c258SEugene Krasnikov HW_RATE_INDEX_54MBPS 5068e84c258SEugene Krasnikov }; 5078e84c258SEugene Krasnikov u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = { 5088e84c258SEugene Krasnikov HW_RATE_INDEX_1MBPS, 5098e84c258SEugene Krasnikov HW_RATE_INDEX_2MBPS, 5108e84c258SEugene Krasnikov HW_RATE_INDEX_5_5MBPS, 5118e84c258SEugene Krasnikov HW_RATE_INDEX_11MBPS 5128e84c258SEugene Krasnikov }; 5138e84c258SEugene Krasnikov 5148e84c258SEugene Krasnikov rates->op_rate_mode = STA_11n; 5158e84c258SEugene Krasnikov memcpy(rates->dsss_rates, dsss_rates, 5168e84c258SEugene Krasnikov sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES); 5178e84c258SEugene Krasnikov memcpy(rates->ofdm_rates, ofdm_rates, 5188e84c258SEugene Krasnikov sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); 5198e84c258SEugene Krasnikov rates->supported_mcs_set[0] = 0xFF; 5208e84c258SEugene Krasnikov } 5218e84c258SEugene Krasnikov static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, 5228e84c258SEugene Krasnikov struct ieee80211_vif *vif, 5238e84c258SEugene Krasnikov struct ieee80211_bss_conf *bss_conf, 5248e84c258SEugene Krasnikov u32 changed) 5258e84c258SEugene Krasnikov { 5268e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 5278e84c258SEugene Krasnikov struct sk_buff *skb = NULL; 5288e84c258SEugene Krasnikov u16 tim_off, tim_len; 5298e84c258SEugene Krasnikov enum wcn36xx_hal_link_state link_state; 5308e84c258SEugene Krasnikov struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; 5318e84c258SEugene Krasnikov 5328e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n", 5338e84c258SEugene Krasnikov vif, changed); 5348e84c258SEugene Krasnikov 5358e84c258SEugene Krasnikov if (changed & BSS_CHANGED_BEACON_INFO) { 5368e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 5378e84c258SEugene Krasnikov "mac bss changed dtim period %d\n", 5388e84c258SEugene Krasnikov bss_conf->dtim_period); 5398e84c258SEugene Krasnikov 5408e84c258SEugene Krasnikov vif_priv->dtim_period = bss_conf->dtim_period; 5418e84c258SEugene Krasnikov } 5428e84c258SEugene Krasnikov 5438e84c258SEugene Krasnikov if (changed & BSS_CHANGED_PS) { 5448e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 5458e84c258SEugene Krasnikov "mac bss PS set %d\n", 5468e84c258SEugene Krasnikov bss_conf->ps); 5478e84c258SEugene Krasnikov if (bss_conf->ps) { 5488e84c258SEugene Krasnikov wcn36xx_pmc_enter_bmps_state(wcn, vif); 5498e84c258SEugene Krasnikov } else { 5508e84c258SEugene Krasnikov wcn36xx_pmc_exit_bmps_state(wcn, vif); 5518e84c258SEugene Krasnikov } 5528e84c258SEugene Krasnikov } 5538e84c258SEugene Krasnikov 5548e84c258SEugene Krasnikov if (changed & BSS_CHANGED_BSSID) { 5558e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n", 5568e84c258SEugene Krasnikov bss_conf->bssid); 5578e84c258SEugene Krasnikov 5588e84c258SEugene Krasnikov if (!is_zero_ether_addr(bss_conf->bssid)) { 5598e84c258SEugene Krasnikov vif_priv->is_joining = true; 5608e84c258SEugene Krasnikov vif_priv->bss_index = 0xff; 5618e84c258SEugene Krasnikov wcn36xx_smd_join(wcn, bss_conf->bssid, 5628e84c258SEugene Krasnikov vif->addr, WCN36XX_HW_CHANNEL(wcn)); 5638e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, vif, NULL, 5648e84c258SEugene Krasnikov bss_conf->bssid, false); 5658e84c258SEugene Krasnikov } else { 5668e84c258SEugene Krasnikov vif_priv->is_joining = false; 5678e84c258SEugene Krasnikov wcn36xx_smd_delete_bss(wcn, vif); 5688e84c258SEugene Krasnikov } 5698e84c258SEugene Krasnikov } 5708e84c258SEugene Krasnikov 5718e84c258SEugene Krasnikov if (changed & BSS_CHANGED_SSID) { 5728e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 5738e84c258SEugene Krasnikov "mac bss changed ssid\n"); 5748e84c258SEugene Krasnikov wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ", 5758e84c258SEugene Krasnikov bss_conf->ssid, bss_conf->ssid_len); 5768e84c258SEugene Krasnikov 5778e84c258SEugene Krasnikov vif_priv->ssid.length = bss_conf->ssid_len; 5788e84c258SEugene Krasnikov memcpy(&vif_priv->ssid.ssid, 5798e84c258SEugene Krasnikov bss_conf->ssid, 5808e84c258SEugene Krasnikov bss_conf->ssid_len); 5818e84c258SEugene Krasnikov } 5828e84c258SEugene Krasnikov 5838e84c258SEugene Krasnikov if (changed & BSS_CHANGED_ASSOC) { 5848e84c258SEugene Krasnikov vif_priv->is_joining = false; 5858e84c258SEugene Krasnikov if (bss_conf->assoc) { 5868e84c258SEugene Krasnikov struct ieee80211_sta *sta; 5878e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv; 5888e84c258SEugene Krasnikov 5898e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 5908e84c258SEugene Krasnikov "mac assoc bss %pM vif %pM AID=%d\n", 5918e84c258SEugene Krasnikov bss_conf->bssid, 5928e84c258SEugene Krasnikov vif->addr, 5938e84c258SEugene Krasnikov bss_conf->aid); 5948e84c258SEugene Krasnikov 5958e84c258SEugene Krasnikov rcu_read_lock(); 5968e84c258SEugene Krasnikov sta = ieee80211_find_sta(vif, bss_conf->bssid); 5978e84c258SEugene Krasnikov if (!sta) { 5988e84c258SEugene Krasnikov wcn36xx_err("sta %pM is not found\n", 5998e84c258SEugene Krasnikov bss_conf->bssid); 6008e84c258SEugene Krasnikov rcu_read_unlock(); 6018e84c258SEugene Krasnikov goto out; 6028e84c258SEugene Krasnikov } 6038e84c258SEugene Krasnikov sta_priv = (struct wcn36xx_sta *)sta->drv_priv; 6048e84c258SEugene Krasnikov 6058e84c258SEugene Krasnikov wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); 6068e84c258SEugene Krasnikov 6078e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, 6088e84c258SEugene Krasnikov vif->addr, 6098e84c258SEugene Krasnikov WCN36XX_HAL_LINK_POSTASSOC_STATE); 6108e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, vif, sta, 6118e84c258SEugene Krasnikov bss_conf->bssid, 6128e84c258SEugene Krasnikov true); 6138e84c258SEugene Krasnikov sta_priv->aid = bss_conf->aid; 6148e84c258SEugene Krasnikov /* 6158e84c258SEugene Krasnikov * config_sta must be called from because this is the 6168e84c258SEugene Krasnikov * place where AID is available. 6178e84c258SEugene Krasnikov */ 6188e84c258SEugene Krasnikov wcn36xx_smd_config_sta(wcn, vif, sta); 6198e84c258SEugene Krasnikov rcu_read_unlock(); 6208e84c258SEugene Krasnikov } else { 6218e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 6228e84c258SEugene Krasnikov "disassociated bss %pM vif %pM AID=%d\n", 6238e84c258SEugene Krasnikov bss_conf->bssid, 6248e84c258SEugene Krasnikov vif->addr, 6258e84c258SEugene Krasnikov bss_conf->aid); 6268e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, 6278e84c258SEugene Krasnikov bss_conf->bssid, 6288e84c258SEugene Krasnikov vif->addr, 6298e84c258SEugene Krasnikov WCN36XX_HAL_LINK_IDLE_STATE); 6308e84c258SEugene Krasnikov } 6318e84c258SEugene Krasnikov } 6328e84c258SEugene Krasnikov 6338e84c258SEugene Krasnikov if (changed & BSS_CHANGED_AP_PROBE_RESP) { 6348e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n"); 6358e84c258SEugene Krasnikov skb = ieee80211_proberesp_get(hw, vif); 6368e84c258SEugene Krasnikov if (!skb) { 6378e84c258SEugene Krasnikov wcn36xx_err("failed to alloc probereq skb\n"); 6388e84c258SEugene Krasnikov goto out; 6398e84c258SEugene Krasnikov } 6408e84c258SEugene Krasnikov 6418e84c258SEugene Krasnikov wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb); 6428e84c258SEugene Krasnikov dev_kfree_skb(skb); 6438e84c258SEugene Krasnikov } 6448e84c258SEugene Krasnikov 645b3e3f871SChun-Yeow Yeoh if (changed & BSS_CHANGED_BEACON_ENABLED || 646b3e3f871SChun-Yeow Yeoh changed & BSS_CHANGED_BEACON) { 6478e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, 6488e84c258SEugene Krasnikov "mac bss changed beacon enabled %d\n", 6498e84c258SEugene Krasnikov bss_conf->enable_beacon); 6508e84c258SEugene Krasnikov 6518e84c258SEugene Krasnikov if (bss_conf->enable_beacon) { 6528e84c258SEugene Krasnikov vif_priv->bss_index = 0xff; 6538e84c258SEugene Krasnikov wcn36xx_smd_config_bss(wcn, vif, NULL, 6548e84c258SEugene Krasnikov vif->addr, false); 6558e84c258SEugene Krasnikov skb = ieee80211_beacon_get_tim(hw, vif, &tim_off, 6568e84c258SEugene Krasnikov &tim_len); 6578e84c258SEugene Krasnikov if (!skb) { 6588e84c258SEugene Krasnikov wcn36xx_err("failed to alloc beacon skb\n"); 6598e84c258SEugene Krasnikov goto out; 6608e84c258SEugene Krasnikov } 6618e84c258SEugene Krasnikov wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0); 6628e84c258SEugene Krasnikov dev_kfree_skb(skb); 6638e84c258SEugene Krasnikov 6648e84c258SEugene Krasnikov if (vif->type == NL80211_IFTYPE_ADHOC || 6658e84c258SEugene Krasnikov vif->type == NL80211_IFTYPE_MESH_POINT) 6668e84c258SEugene Krasnikov link_state = WCN36XX_HAL_LINK_IBSS_STATE; 6678e84c258SEugene Krasnikov else 6688e84c258SEugene Krasnikov link_state = WCN36XX_HAL_LINK_AP_STATE; 6698e84c258SEugene Krasnikov 6708e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, 6718e84c258SEugene Krasnikov link_state); 6728e84c258SEugene Krasnikov } else { 6738e84c258SEugene Krasnikov wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, 6748e84c258SEugene Krasnikov WCN36XX_HAL_LINK_IDLE_STATE); 6758e84c258SEugene Krasnikov wcn36xx_smd_delete_bss(wcn, vif); 6768e84c258SEugene Krasnikov } 6778e84c258SEugene Krasnikov } 6788e84c258SEugene Krasnikov out: 6798e84c258SEugene Krasnikov return; 6808e84c258SEugene Krasnikov } 6818e84c258SEugene Krasnikov 6828e84c258SEugene Krasnikov /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ 6838e84c258SEugene Krasnikov static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) 6848e84c258SEugene Krasnikov { 6858e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 6868e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value); 6878e84c258SEugene Krasnikov 6888e84c258SEugene Krasnikov wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); 6898e84c258SEugene Krasnikov return 0; 6908e84c258SEugene Krasnikov } 6918e84c258SEugene Krasnikov 6928e84c258SEugene Krasnikov static void wcn36xx_remove_interface(struct ieee80211_hw *hw, 6938e84c258SEugene Krasnikov struct ieee80211_vif *vif) 6948e84c258SEugene Krasnikov { 6958e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 6968e84c258SEugene Krasnikov struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; 6978e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif); 6988e84c258SEugene Krasnikov 6998e84c258SEugene Krasnikov list_del(&vif_priv->list); 7008e84c258SEugene Krasnikov wcn36xx_smd_delete_sta_self(wcn, vif->addr); 7018e84c258SEugene Krasnikov } 7028e84c258SEugene Krasnikov 7038e84c258SEugene Krasnikov static int wcn36xx_add_interface(struct ieee80211_hw *hw, 7048e84c258SEugene Krasnikov struct ieee80211_vif *vif) 7058e84c258SEugene Krasnikov { 7068e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 7078e84c258SEugene Krasnikov struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; 7088e84c258SEugene Krasnikov 7098e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n", 7108e84c258SEugene Krasnikov vif, vif->type); 7118e84c258SEugene Krasnikov 7128e84c258SEugene Krasnikov if (!(NL80211_IFTYPE_STATION == vif->type || 7138e84c258SEugene Krasnikov NL80211_IFTYPE_AP == vif->type || 7148e84c258SEugene Krasnikov NL80211_IFTYPE_ADHOC == vif->type || 7158e84c258SEugene Krasnikov NL80211_IFTYPE_MESH_POINT == vif->type)) { 7168e84c258SEugene Krasnikov wcn36xx_warn("Unsupported interface type requested: %d\n", 7178e84c258SEugene Krasnikov vif->type); 7188e84c258SEugene Krasnikov return -EOPNOTSUPP; 7198e84c258SEugene Krasnikov } 7208e84c258SEugene Krasnikov 7218e84c258SEugene Krasnikov list_add(&vif_priv->list, &wcn->vif_list); 7228e84c258SEugene Krasnikov wcn36xx_smd_add_sta_self(wcn, vif); 7238e84c258SEugene Krasnikov 7248e84c258SEugene Krasnikov return 0; 7258e84c258SEugene Krasnikov } 7268e84c258SEugene Krasnikov 7278e84c258SEugene Krasnikov static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 7288e84c258SEugene Krasnikov struct ieee80211_sta *sta) 7298e84c258SEugene Krasnikov { 7308e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 7318e84c258SEugene Krasnikov struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; 7328e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; 7338e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", 7348e84c258SEugene Krasnikov vif, sta->addr); 7358e84c258SEugene Krasnikov 7368e84c258SEugene Krasnikov vif_priv->sta = sta_priv; 7378e84c258SEugene Krasnikov sta_priv->vif = vif_priv; 7388e84c258SEugene Krasnikov /* 7398e84c258SEugene Krasnikov * For STA mode HW will be configured on BSS_CHANGED_ASSOC because 7408e84c258SEugene Krasnikov * at this stage AID is not available yet. 7418e84c258SEugene Krasnikov */ 7428e84c258SEugene Krasnikov if (NL80211_IFTYPE_STATION != vif->type) { 7438e84c258SEugene Krasnikov wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); 7448e84c258SEugene Krasnikov sta_priv->aid = sta->aid; 7458e84c258SEugene Krasnikov wcn36xx_smd_config_sta(wcn, vif, sta); 7468e84c258SEugene Krasnikov } 7478e84c258SEugene Krasnikov return 0; 7488e84c258SEugene Krasnikov } 7498e84c258SEugene Krasnikov 7508e84c258SEugene Krasnikov static int wcn36xx_sta_remove(struct ieee80211_hw *hw, 7518e84c258SEugene Krasnikov struct ieee80211_vif *vif, 7528e84c258SEugene Krasnikov struct ieee80211_sta *sta) 7538e84c258SEugene Krasnikov { 7548e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 7558e84c258SEugene Krasnikov struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; 7568e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; 7578e84c258SEugene Krasnikov 7588e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n", 7598e84c258SEugene Krasnikov vif, sta->addr, sta_priv->sta_index); 7608e84c258SEugene Krasnikov 7618e84c258SEugene Krasnikov wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index); 7628e84c258SEugene Krasnikov vif_priv->sta = NULL; 7638e84c258SEugene Krasnikov sta_priv->vif = NULL; 7648e84c258SEugene Krasnikov return 0; 7658e84c258SEugene Krasnikov } 7668e84c258SEugene Krasnikov 7678e84c258SEugene Krasnikov #ifdef CONFIG_PM 7688e84c258SEugene Krasnikov 7698e84c258SEugene Krasnikov static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) 7708e84c258SEugene Krasnikov { 7718e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 7728e84c258SEugene Krasnikov 7738e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n"); 7748e84c258SEugene Krasnikov 7758e84c258SEugene Krasnikov flush_workqueue(wcn->hal_ind_wq); 7768e84c258SEugene Krasnikov wcn36xx_smd_set_power_params(wcn, true); 7778e84c258SEugene Krasnikov return 0; 7788e84c258SEugene Krasnikov } 7798e84c258SEugene Krasnikov 7808e84c258SEugene Krasnikov static int wcn36xx_resume(struct ieee80211_hw *hw) 7818e84c258SEugene Krasnikov { 7828e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 7838e84c258SEugene Krasnikov 7848e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n"); 7858e84c258SEugene Krasnikov 7868e84c258SEugene Krasnikov flush_workqueue(wcn->hal_ind_wq); 7878e84c258SEugene Krasnikov wcn36xx_smd_set_power_params(wcn, false); 7888e84c258SEugene Krasnikov return 0; 7898e84c258SEugene Krasnikov } 7908e84c258SEugene Krasnikov 7918e84c258SEugene Krasnikov #endif 7928e84c258SEugene Krasnikov 7938e84c258SEugene Krasnikov static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, 7948e84c258SEugene Krasnikov struct ieee80211_vif *vif, 7958e84c258SEugene Krasnikov enum ieee80211_ampdu_mlme_action action, 7968e84c258SEugene Krasnikov struct ieee80211_sta *sta, u16 tid, u16 *ssn, 7978e84c258SEugene Krasnikov u8 buf_size) 7988e84c258SEugene Krasnikov { 7998e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 8008e84c258SEugene Krasnikov struct wcn36xx_sta *sta_priv = NULL; 8018e84c258SEugene Krasnikov 8028e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", 8038e84c258SEugene Krasnikov action, tid); 8048e84c258SEugene Krasnikov 8058e84c258SEugene Krasnikov sta_priv = (struct wcn36xx_sta *)sta->drv_priv; 8068e84c258SEugene Krasnikov 8078e84c258SEugene Krasnikov switch (action) { 8088e84c258SEugene Krasnikov case IEEE80211_AMPDU_RX_START: 8098e84c258SEugene Krasnikov sta_priv->tid = tid; 8108e84c258SEugene Krasnikov wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, 8118e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 8128e84c258SEugene Krasnikov wcn36xx_smd_add_ba(wcn); 8138e84c258SEugene Krasnikov wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); 8148e84c258SEugene Krasnikov ieee80211_start_tx_ba_session(sta, tid, 0); 8158e84c258SEugene Krasnikov break; 8168e84c258SEugene Krasnikov case IEEE80211_AMPDU_RX_STOP: 8178e84c258SEugene Krasnikov wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); 8188e84c258SEugene Krasnikov break; 8198e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_START: 8208e84c258SEugene Krasnikov ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); 8218e84c258SEugene Krasnikov break; 8228e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_OPERATIONAL: 8238e84c258SEugene Krasnikov wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, 8248e84c258SEugene Krasnikov get_sta_index(vif, sta_priv)); 8258e84c258SEugene Krasnikov break; 8268e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_STOP_FLUSH: 8278e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: 8288e84c258SEugene Krasnikov case IEEE80211_AMPDU_TX_STOP_CONT: 8298e84c258SEugene Krasnikov ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 8308e84c258SEugene Krasnikov break; 8318e84c258SEugene Krasnikov default: 8328e84c258SEugene Krasnikov wcn36xx_err("Unknown AMPDU action\n"); 8338e84c258SEugene Krasnikov } 8348e84c258SEugene Krasnikov 8358e84c258SEugene Krasnikov return 0; 8368e84c258SEugene Krasnikov } 8378e84c258SEugene Krasnikov 8388e84c258SEugene Krasnikov static const struct ieee80211_ops wcn36xx_ops = { 8398e84c258SEugene Krasnikov .start = wcn36xx_start, 8408e84c258SEugene Krasnikov .stop = wcn36xx_stop, 8418e84c258SEugene Krasnikov .add_interface = wcn36xx_add_interface, 8428e84c258SEugene Krasnikov .remove_interface = wcn36xx_remove_interface, 8438e84c258SEugene Krasnikov #ifdef CONFIG_PM 8448e84c258SEugene Krasnikov .suspend = wcn36xx_suspend, 8458e84c258SEugene Krasnikov .resume = wcn36xx_resume, 8468e84c258SEugene Krasnikov #endif 8478e84c258SEugene Krasnikov .config = wcn36xx_config, 8488e84c258SEugene Krasnikov .configure_filter = wcn36xx_configure_filter, 8498e84c258SEugene Krasnikov .tx = wcn36xx_tx, 8508e84c258SEugene Krasnikov .set_key = wcn36xx_set_key, 8518e84c258SEugene Krasnikov .sw_scan_start = wcn36xx_sw_scan_start, 8528e84c258SEugene Krasnikov .sw_scan_complete = wcn36xx_sw_scan_complete, 8538e84c258SEugene Krasnikov .bss_info_changed = wcn36xx_bss_info_changed, 8548e84c258SEugene Krasnikov .set_rts_threshold = wcn36xx_set_rts_threshold, 8558e84c258SEugene Krasnikov .sta_add = wcn36xx_sta_add, 8568e84c258SEugene Krasnikov .sta_remove = wcn36xx_sta_remove, 8578e84c258SEugene Krasnikov .ampdu_action = wcn36xx_ampdu_action, 8588e84c258SEugene Krasnikov }; 8598e84c258SEugene Krasnikov 8608e84c258SEugene Krasnikov static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) 8618e84c258SEugene Krasnikov { 8628e84c258SEugene Krasnikov int ret = 0; 8638e84c258SEugene Krasnikov 8648e84c258SEugene Krasnikov static const u32 cipher_suites[] = { 8658e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_WEP40, 8668e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_WEP104, 8678e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_TKIP, 8688e84c258SEugene Krasnikov WLAN_CIPHER_SUITE_CCMP, 8698e84c258SEugene Krasnikov }; 8708e84c258SEugene Krasnikov 8718e84c258SEugene Krasnikov wcn->hw->flags = IEEE80211_HW_SIGNAL_DBM | 8728e84c258SEugene Krasnikov IEEE80211_HW_HAS_RATE_CONTROL | 8738e84c258SEugene Krasnikov IEEE80211_HW_SUPPORTS_PS | 8748e84c258SEugene Krasnikov IEEE80211_HW_CONNECTION_MONITOR | 8758e84c258SEugene Krasnikov IEEE80211_HW_AMPDU_AGGREGATION | 8768e84c258SEugene Krasnikov IEEE80211_HW_TIMING_BEACON_ONLY; 8778e84c258SEugene Krasnikov 8788e84c258SEugene Krasnikov wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 8798e84c258SEugene Krasnikov BIT(NL80211_IFTYPE_AP) | 8808e84c258SEugene Krasnikov BIT(NL80211_IFTYPE_ADHOC) | 8818e84c258SEugene Krasnikov BIT(NL80211_IFTYPE_MESH_POINT); 8828e84c258SEugene Krasnikov 8838e84c258SEugene Krasnikov wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz; 8848e84c258SEugene Krasnikov wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz; 8858e84c258SEugene Krasnikov 8868e84c258SEugene Krasnikov wcn->hw->wiphy->cipher_suites = cipher_suites; 8878e84c258SEugene Krasnikov wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); 8888e84c258SEugene Krasnikov 8898e84c258SEugene Krasnikov wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; 8908e84c258SEugene Krasnikov 8918e84c258SEugene Krasnikov #ifdef CONFIG_PM 8928e84c258SEugene Krasnikov wcn->hw->wiphy->wowlan = &wowlan_support; 8938e84c258SEugene Krasnikov #endif 8948e84c258SEugene Krasnikov 8958e84c258SEugene Krasnikov wcn->hw->max_listen_interval = 200; 8968e84c258SEugene Krasnikov 8978e84c258SEugene Krasnikov wcn->hw->queues = 4; 8988e84c258SEugene Krasnikov 8998e84c258SEugene Krasnikov SET_IEEE80211_DEV(wcn->hw, wcn->dev); 9008e84c258SEugene Krasnikov 9018e84c258SEugene Krasnikov wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); 9028e84c258SEugene Krasnikov wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); 9038e84c258SEugene Krasnikov 9048e84c258SEugene Krasnikov return ret; 9058e84c258SEugene Krasnikov } 9068e84c258SEugene Krasnikov 9078e84c258SEugene Krasnikov static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, 9088e84c258SEugene Krasnikov struct platform_device *pdev) 9098e84c258SEugene Krasnikov { 9108e84c258SEugene Krasnikov struct resource *res; 9118e84c258SEugene Krasnikov /* Set TX IRQ */ 9128e84c258SEugene Krasnikov res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 9138e84c258SEugene Krasnikov "wcnss_wlantx_irq"); 9148e84c258SEugene Krasnikov if (!res) { 9158e84c258SEugene Krasnikov wcn36xx_err("failed to get tx_irq\n"); 9168e84c258SEugene Krasnikov return -ENOENT; 9178e84c258SEugene Krasnikov } 9188e84c258SEugene Krasnikov wcn->tx_irq = res->start; 9198e84c258SEugene Krasnikov 9208e84c258SEugene Krasnikov /* Set RX IRQ */ 9218e84c258SEugene Krasnikov res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 9228e84c258SEugene Krasnikov "wcnss_wlanrx_irq"); 9238e84c258SEugene Krasnikov if (!res) { 9248e84c258SEugene Krasnikov wcn36xx_err("failed to get rx_irq\n"); 9258e84c258SEugene Krasnikov return -ENOENT; 9268e84c258SEugene Krasnikov } 9278e84c258SEugene Krasnikov wcn->rx_irq = res->start; 9288e84c258SEugene Krasnikov 9298e84c258SEugene Krasnikov /* Map the memory */ 9308e84c258SEugene Krasnikov res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 9318e84c258SEugene Krasnikov "wcnss_mmio"); 9328e84c258SEugene Krasnikov if (!res) { 9338e84c258SEugene Krasnikov wcn36xx_err("failed to get mmio\n"); 9348e84c258SEugene Krasnikov return -ENOENT; 9358e84c258SEugene Krasnikov } 9368e84c258SEugene Krasnikov wcn->mmio = ioremap(res->start, resource_size(res)); 9378e84c258SEugene Krasnikov if (!wcn->mmio) { 9388e84c258SEugene Krasnikov wcn36xx_err("failed to map io memory\n"); 9398e84c258SEugene Krasnikov return -ENOMEM; 9408e84c258SEugene Krasnikov } 9418e84c258SEugene Krasnikov return 0; 9428e84c258SEugene Krasnikov } 9438e84c258SEugene Krasnikov 9448e84c258SEugene Krasnikov static int wcn36xx_probe(struct platform_device *pdev) 9458e84c258SEugene Krasnikov { 9468e84c258SEugene Krasnikov struct ieee80211_hw *hw; 9478e84c258SEugene Krasnikov struct wcn36xx *wcn; 9488e84c258SEugene Krasnikov int ret; 9498e84c258SEugene Krasnikov u8 addr[ETH_ALEN]; 9508e84c258SEugene Krasnikov 9518e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); 9528e84c258SEugene Krasnikov 9538e84c258SEugene Krasnikov hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); 9548e84c258SEugene Krasnikov if (!hw) { 9558e84c258SEugene Krasnikov wcn36xx_err("failed to alloc hw\n"); 9568e84c258SEugene Krasnikov ret = -ENOMEM; 9578e84c258SEugene Krasnikov goto out_err; 9588e84c258SEugene Krasnikov } 9598e84c258SEugene Krasnikov platform_set_drvdata(pdev, hw); 9608e84c258SEugene Krasnikov wcn = hw->priv; 9618e84c258SEugene Krasnikov wcn->hw = hw; 9628e84c258SEugene Krasnikov wcn->dev = &pdev->dev; 9638e84c258SEugene Krasnikov wcn->ctrl_ops = pdev->dev.platform_data; 9648e84c258SEugene Krasnikov 9658e84c258SEugene Krasnikov mutex_init(&wcn->hal_mutex); 9668e84c258SEugene Krasnikov 9678e84c258SEugene Krasnikov if (!wcn->ctrl_ops->get_hw_mac(addr)) { 9688e84c258SEugene Krasnikov wcn36xx_info("mac address: %pM\n", addr); 9698e84c258SEugene Krasnikov SET_IEEE80211_PERM_ADDR(wcn->hw, addr); 9708e84c258SEugene Krasnikov } 9718e84c258SEugene Krasnikov 9728e84c258SEugene Krasnikov ret = wcn36xx_platform_get_resources(wcn, pdev); 9738e84c258SEugene Krasnikov if (ret) 9748e84c258SEugene Krasnikov goto out_wq; 9758e84c258SEugene Krasnikov 9768e84c258SEugene Krasnikov wcn36xx_init_ieee80211(wcn); 9778e84c258SEugene Krasnikov ret = ieee80211_register_hw(wcn->hw); 9788e84c258SEugene Krasnikov if (ret) 9798e84c258SEugene Krasnikov goto out_unmap; 9808e84c258SEugene Krasnikov 9818e84c258SEugene Krasnikov return 0; 9828e84c258SEugene Krasnikov 9838e84c258SEugene Krasnikov out_unmap: 9848e84c258SEugene Krasnikov iounmap(wcn->mmio); 9858e84c258SEugene Krasnikov out_wq: 9868e84c258SEugene Krasnikov ieee80211_free_hw(hw); 9878e84c258SEugene Krasnikov out_err: 9888e84c258SEugene Krasnikov return ret; 9898e84c258SEugene Krasnikov } 9908e84c258SEugene Krasnikov static int wcn36xx_remove(struct platform_device *pdev) 9918e84c258SEugene Krasnikov { 9928e84c258SEugene Krasnikov struct ieee80211_hw *hw = platform_get_drvdata(pdev); 9938e84c258SEugene Krasnikov struct wcn36xx *wcn = hw->priv; 9948e84c258SEugene Krasnikov wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n"); 9958e84c258SEugene Krasnikov 9964bda7fafSPontus Fuchs release_firmware(wcn->nv); 9978e84c258SEugene Krasnikov mutex_destroy(&wcn->hal_mutex); 9988e84c258SEugene Krasnikov 9998e84c258SEugene Krasnikov ieee80211_unregister_hw(hw); 10008e84c258SEugene Krasnikov iounmap(wcn->mmio); 10018e84c258SEugene Krasnikov ieee80211_free_hw(hw); 10028e84c258SEugene Krasnikov 10038e84c258SEugene Krasnikov return 0; 10048e84c258SEugene Krasnikov } 10058e84c258SEugene Krasnikov static const struct platform_device_id wcn36xx_platform_id_table[] = { 10068e84c258SEugene Krasnikov { 10078e84c258SEugene Krasnikov .name = "wcn36xx", 10088e84c258SEugene Krasnikov .driver_data = 0 10098e84c258SEugene Krasnikov }, 10108e84c258SEugene Krasnikov {} 10118e84c258SEugene Krasnikov }; 10128e84c258SEugene Krasnikov MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table); 10138e84c258SEugene Krasnikov 10148e84c258SEugene Krasnikov static struct platform_driver wcn36xx_driver = { 10158e84c258SEugene Krasnikov .probe = wcn36xx_probe, 10168e84c258SEugene Krasnikov .remove = wcn36xx_remove, 10178e84c258SEugene Krasnikov .driver = { 10188e84c258SEugene Krasnikov .name = "wcn36xx", 10198e84c258SEugene Krasnikov .owner = THIS_MODULE, 10208e84c258SEugene Krasnikov }, 10218e84c258SEugene Krasnikov .id_table = wcn36xx_platform_id_table, 10228e84c258SEugene Krasnikov }; 10238e84c258SEugene Krasnikov 10248e84c258SEugene Krasnikov static int __init wcn36xx_init(void) 10258e84c258SEugene Krasnikov { 10268e84c258SEugene Krasnikov platform_driver_register(&wcn36xx_driver); 10278e84c258SEugene Krasnikov return 0; 10288e84c258SEugene Krasnikov } 10298e84c258SEugene Krasnikov module_init(wcn36xx_init); 10308e84c258SEugene Krasnikov 10318e84c258SEugene Krasnikov static void __exit wcn36xx_exit(void) 10328e84c258SEugene Krasnikov { 10338e84c258SEugene Krasnikov platform_driver_unregister(&wcn36xx_driver); 10348e84c258SEugene Krasnikov } 10358e84c258SEugene Krasnikov module_exit(wcn36xx_exit); 10368e84c258SEugene Krasnikov 10378e84c258SEugene Krasnikov MODULE_LICENSE("Dual BSD/GPL"); 10388e84c258SEugene Krasnikov MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); 10398e84c258SEugene Krasnikov MODULE_FIRMWARE(WLAN_NV_FILE); 1040