// SPDX-License-Identifier: GPL-2.0 /****************************************************************************** * * Copyright (C) 2020 SeekWave Technology Co.,Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * ******************************************************************************/ #include #include #include #include #include #include "skw_core.h" #include "skw_iface.h" #include "skw_msg.h" #include "skw_cfg80211.h" #include "skw_regd.h" #include "skw_mlme.h" #include "skw_timer.h" #include "skw_work.h" #include "skw_tdls.h" #include "skw_calib.h" #include "skw_recovery.h" #include "skw_dfs.h" #define SKW_BIT_ULL(nr) (1ULL << (nr)) int to_skw_bw(enum nl80211_chan_width bw) { switch (bw) { case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: return SKW_CHAN_WIDTH_20; case NL80211_CHAN_WIDTH_40: return SKW_CHAN_WIDTH_40; case NL80211_CHAN_WIDTH_80: return SKW_CHAN_WIDTH_80; case NL80211_CHAN_WIDTH_80P80: return SKW_CHAN_WIDTH_80P80; case NL80211_CHAN_WIDTH_160: return SKW_CHAN_WIDTH_160; default: break; } return SKW_CHAN_WIDTH_MAX; } static int to_skw_gtk(u8 key_index) { switch (key_index) { case 0 ... 3: return SKW_KEY_TYPE_GTK; case 4 ... 5: return SKW_KEY_TYPE_IGTK; case 6: return SKW_KEY_TYPE_BIGTK; default: break; } return SKW_KEY_TYPE_GTK; } static int to_skw_cipher_type(u32 cipher) { #define SKW_CASE_CIPHER_TYPE(c) \ { \ case SKW_CIPHER_SUITE_##c: \ return SKW_CIPHER_TYPE_##c; \ } switch (cipher) { SKW_CASE_CIPHER_TYPE(WEP40); SKW_CASE_CIPHER_TYPE(WEP104); SKW_CASE_CIPHER_TYPE(SMS4); SKW_CASE_CIPHER_TYPE(TKIP); SKW_CASE_CIPHER_TYPE(CCMP); SKW_CASE_CIPHER_TYPE(CCMP_256); SKW_CASE_CIPHER_TYPE(AES_CMAC); SKW_CASE_CIPHER_TYPE(BIP_CMAC_256); SKW_CASE_CIPHER_TYPE(BIP_GMAC_128); SKW_CASE_CIPHER_TYPE(BIP_GMAC_256); SKW_CASE_CIPHER_TYPE(GCMP); SKW_CASE_CIPHER_TYPE(GCMP_256); default: break; } #undef SKW_CASE_CIPHER_TYPE return SKW_CIPHER_TYPE_INVALID; } static const struct ieee80211_iface_limit skw_iface_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT), }, #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE), }, #endif }; static const struct ieee80211_iface_limit skw_iface_limits_change[] = { { .max = 3, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT), }, #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE), }, #endif }; static const struct ieee80211_iface_limit skw_iface_limits_aps[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT), }, { .max = 2, .types = BIT(NL80211_IFTYPE_AP), }, }; static const struct ieee80211_iface_limit skw_iface_limits_monitor[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_MONITOR), }, }; #ifdef CONFIG_SWT6621S_DFS_MASTER static const struct ieee80211_iface_limit skw_iface_limits_dfs[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, }; static const struct ieee80211_iface_limit skw_iface_limits_dfs_change[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION), }, }; #endif static const struct ieee80211_iface_combination skw_iface_combos[] = { { #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) .max_interfaces = 4, #else .max_interfaces = 3, #endif .num_different_channels = 2, .limits = skw_iface_limits, .n_limits = ARRAY_SIZE(skw_iface_limits), }, { #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 10) .max_interfaces = 4, #else .max_interfaces = 3, #endif .num_different_channels = 2, .limits = skw_iface_limits_change, .n_limits = ARRAY_SIZE(skw_iface_limits_change), }, { .max_interfaces = 3, .num_different_channels = 1, .limits = skw_iface_limits_aps, .n_limits = ARRAY_SIZE(skw_iface_limits_aps), }, { .max_interfaces = 2, .num_different_channels = 1, .limits = skw_iface_limits_monitor, .n_limits = ARRAY_SIZE(skw_iface_limits_monitor), }, #ifdef CONFIG_SWT6621S_DFS_MASTER { .max_interfaces = 2, .num_different_channels = 1, .limits = skw_iface_limits_dfs, .n_limits = ARRAY_SIZE(skw_iface_limits_dfs), .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80), }, { .max_interfaces = 2, .num_different_channels = 1, .limits = skw_iface_limits_dfs_change, .n_limits = ARRAY_SIZE(skw_iface_limits_dfs_change), .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | BIT(NL80211_CHAN_WIDTH_20) | BIT(NL80211_CHAN_WIDTH_40) | BIT(NL80211_CHAN_WIDTH_80), }, #endif }; static const struct ieee80211_txrx_stypes skw_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4), }, [NL80211_IFTYPE_AP] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4), }, [NL80211_IFTYPE_P2P_CLIENT] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, [NL80211_IFTYPE_P2P_GO] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4), }, [NL80211_IFTYPE_P2P_DEVICE] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4), }, }; #define SKW_CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel skw_2ghz_chan[] = { SKW_CHAN2G(1, 2412, 0), SKW_CHAN2G(2, 2417, 0), SKW_CHAN2G(3, 2422, 0), SKW_CHAN2G(4, 2427, 0), SKW_CHAN2G(5, 2432, 0), SKW_CHAN2G(6, 2437, 0), SKW_CHAN2G(7, 2442, 0), SKW_CHAN2G(8, 2447, 0), SKW_CHAN2G(9, 2452, 0), SKW_CHAN2G(10, 2457, 0), SKW_CHAN2G(11, 2462, 0), SKW_CHAN2G(12, 2467, 0), SKW_CHAN2G(13, 2472, 0), SKW_CHAN2G(14, 2484, 0), }; #undef SKW_CHAN2G #define SKW_CHAN5G(_channel, _flags) { \ .band = NL80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel skw_5ghz_chan[] = { SKW_CHAN5G(36, 0), SKW_CHAN5G(40, 0), SKW_CHAN5G(44, 0), SKW_CHAN5G(48, 0), SKW_CHAN5G(52, 0), SKW_CHAN5G(56, 0), SKW_CHAN5G(60, 0), SKW_CHAN5G(64, 0), SKW_CHAN5G(100, 0), SKW_CHAN5G(104, 0), SKW_CHAN5G(108, 0), SKW_CHAN5G(112, 0), SKW_CHAN5G(116, 0), SKW_CHAN5G(120, 0), SKW_CHAN5G(124, 0), SKW_CHAN5G(128, 0), SKW_CHAN5G(132, 0), SKW_CHAN5G(136, 0), SKW_CHAN5G(140, 0), SKW_CHAN5G(144, 0), SKW_CHAN5G(149, 0), SKW_CHAN5G(153, 0), SKW_CHAN5G(157, 0), SKW_CHAN5G(161, 0), SKW_CHAN5G(165, 0), SKW_CHAN5G(169, 0), SKW_CHAN5G(173, 0), SKW_CHAN5G(177, 0), SKW_CHAN5G(181, 0), }; #undef SKW_CHAN5G #ifdef CONFIG_SWT6621S_6GHZ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) #define SKW_CHAN6G(_channel, _flags) { \ .band = NL80211_BAND_6GHZ, \ .center_freq = 5950 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel skw_6ghz_chan[] = { SKW_CHAN6G(1, 0), SKW_CHAN6G(2, 0), SKW_CHAN6G(5, 0), SKW_CHAN6G(9, 0), SKW_CHAN6G(13, 0), SKW_CHAN6G(17, 0), SKW_CHAN6G(21, 0), SKW_CHAN6G(25, 0), SKW_CHAN6G(29, 0), SKW_CHAN6G(33, 0), SKW_CHAN6G(37, 0), SKW_CHAN6G(41, 0), SKW_CHAN6G(45, 0), SKW_CHAN6G(49, 0), SKW_CHAN6G(53, 0), SKW_CHAN6G(57, 0), SKW_CHAN6G(61, 0), SKW_CHAN6G(65, 0), SKW_CHAN6G(69, 0), SKW_CHAN6G(73, 0), SKW_CHAN6G(77, 0), SKW_CHAN6G(81, 0), SKW_CHAN6G(85, 0), SKW_CHAN6G(89, 0), SKW_CHAN6G(93, 0), SKW_CHAN6G(97, 0), SKW_CHAN6G(101, 0), SKW_CHAN6G(105, 0), SKW_CHAN6G(109, 0), SKW_CHAN6G(113, 0), SKW_CHAN6G(117, 0), SKW_CHAN6G(121, 0), SKW_CHAN6G(125, 0), SKW_CHAN6G(129, 0), SKW_CHAN6G(133, 0), SKW_CHAN6G(137, 0), SKW_CHAN6G(141, 0), SKW_CHAN6G(145, 0), SKW_CHAN6G(149, 0), SKW_CHAN6G(153, 0), SKW_CHAN6G(157, 0), SKW_CHAN6G(161, 0), SKW_CHAN6G(165, 0), SKW_CHAN6G(169, 0), SKW_CHAN6G(173, 0), SKW_CHAN6G(177, 0), SKW_CHAN6G(181, 0), SKW_CHAN6G(185, 0), SKW_CHAN6G(189, 0), SKW_CHAN6G(193, 0), SKW_CHAN6G(197, 0), SKW_CHAN6G(201, 0), SKW_CHAN6G(205, 0), SKW_CHAN6G(209, 0), SKW_CHAN6G(213, 0), SKW_CHAN6G(217, 0), SKW_CHAN6G(221, 0), SKW_CHAN6G(225, 0), SKW_CHAN6G(229, 0), SKW_CHAN6G(233, 0), }; #undef SKW_CHAN6G #endif #endif #define SKW_RATETAB_ENT(_rate, _rateid, _flags) \ { \ .bitrate = (_rate), \ .hw_value = (_rateid), \ .flags = (_flags), \ } static struct ieee80211_rate skw_rates[] = { SKW_RATETAB_ENT(10, 0x1, 0), SKW_RATETAB_ENT(20, 0x2, 0), SKW_RATETAB_ENT(55, 0x5, 0), SKW_RATETAB_ENT(110, 0xb, 0), SKW_RATETAB_ENT(60, 0x6, 0), SKW_RATETAB_ENT(90, 0x9, 0), SKW_RATETAB_ENT(120, 0xc, 0), SKW_RATETAB_ENT(180, 0x12, 0), SKW_RATETAB_ENT(240, 0x18, 0), SKW_RATETAB_ENT(360, 0x24, 0), SKW_RATETAB_ENT(480, 0x30, 0), SKW_RATETAB_ENT(540, 0x36, 0), }; #undef SKW_RATETAB_ENT #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) static const struct ieee80211_sband_iftype_data skw_he_capa_2ghz = { .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), .he_cap = { .has_he = true, .he_cap_elem = { .mac_cap_info[0] = SKW_HE_MAC_CAP0_HTC_HE, .mac_cap_info[1] = SKW_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | SKW_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, .mac_cap_info[2] = SKW_HE_MAC_CAP2_BSR | SKW_HE_MAC_CAP2_MU_CASCADING | SKW_HE_MAC_CAP2_ACK_EN, .mac_cap_info[3] = SKW_HE_MAC_CAP3_OMI_CONTROL | SKW_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU | SKW_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2, .mac_cap_info[4] = SKW_HE_MAC_CAP4_AMDSU_IN_AMPDU, .phy_cap_info[0] = SKW_HE_PHY_CAP0_DUAL_BAND | SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G, .phy_cap_info[1] = SKW_HE_PHY_CAP1_DEVICE_CLASS_A | SKW_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | SKW_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | SKW_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, .phy_cap_info[2] = SKW_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | SKW_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | SKW_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | SKW_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | SKW_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, }, .he_mcs_nss_supp = { .rx_mcs_80 = cpu_to_le16(0xfffa), .tx_mcs_80 = cpu_to_le16(0xfffa), .rx_mcs_160 = cpu_to_le16(0xffff), .tx_mcs_160 = cpu_to_le16(0xffff), .rx_mcs_80p80 = cpu_to_le16(0xffff), .tx_mcs_80p80 = cpu_to_le16(0xffff), }, }, }; static const struct ieee80211_sband_iftype_data skw_he_capa_5ghz = { .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), .he_cap = { .has_he = true, .he_cap_elem = { .mac_cap_info[0] = SKW_HE_MAC_CAP0_HTC_HE, .mac_cap_info[1] = SKW_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US | SKW_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, .mac_cap_info[2] = SKW_HE_MAC_CAP2_BSR | SKW_HE_MAC_CAP2_MU_CASCADING | SKW_HE_MAC_CAP2_ACK_EN, .mac_cap_info[3] = SKW_HE_MAC_CAP3_OMI_CONTROL | SKW_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU | SKW_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2, .mac_cap_info[4] = SKW_HE_MAC_CAP4_AMDSU_IN_AMPDU, .phy_cap_info[0] = SKW_HE_PHY_CAP0_DUAL_BAND | SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G, .phy_cap_info[1] = SKW_HE_PHY_CAP1_DEVICE_CLASS_A | SKW_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | SKW_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | SKW_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS, .phy_cap_info[2] = SKW_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | SKW_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | SKW_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | SKW_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | SKW_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO, }, .he_mcs_nss_supp = { .rx_mcs_80 = cpu_to_le16(0xfffa), .tx_mcs_80 = cpu_to_le16(0xfffa), .rx_mcs_160 = cpu_to_le16(0xffff), .tx_mcs_160 = cpu_to_le16(0xffff), .rx_mcs_80p80 = cpu_to_le16(0xffff), .tx_mcs_80p80 = cpu_to_le16(0xffff), }, }, }; #endif #define skw_a_rates (skw_rates + 4) #define skw_a_rates_size 8 #define skw_g_rates (skw_rates + 0) #define skw_g_rates_size 12 static struct ieee80211_supported_band skw_band_2ghz = { .channels = skw_2ghz_chan, .n_channels = ARRAY_SIZE(skw_2ghz_chan), .bitrates = skw_g_rates, .n_bitrates = skw_g_rates_size, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) .n_iftype_data = 1, .iftype_data = &skw_he_capa_2ghz, #endif }; static struct ieee80211_supported_band skw_band_5ghz = { .channels = skw_5ghz_chan, .n_channels = ARRAY_SIZE(skw_5ghz_chan), .bitrates = skw_a_rates, .n_bitrates = skw_a_rates_size, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) .n_iftype_data = 1, .iftype_data = &skw_he_capa_5ghz, #endif }; #ifdef CONFIG_SWT6621S_6GHZ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) static struct ieee80211_supported_band skw_band_6ghz = { .channels = skw_6ghz_chan, .n_channels = ARRAY_SIZE(skw_6ghz_chan), .bitrates = skw_a_rates, .n_bitrates = skw_a_rates_size, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) .n_iftype_data = 1, .iftype_data = &skw_he_capa_5ghz, //TBD:check it #endif }; #endif #endif static const u32 skw_cipher_suites[] = { /* keep WEP first, it may be removed below */ SKW_CIPHER_SUITE_WEP40, SKW_CIPHER_SUITE_TKIP, SKW_CIPHER_SUITE_CCMP, SKW_CIPHER_SUITE_WEP104, SKW_CIPHER_SUITE_AES_CMAC, SKW_CIPHER_SUITE_GCMP, SKW_CIPHER_SUITE_CCMP_256, SKW_CIPHER_SUITE_GCMP_256, SKW_CIPHER_SUITE_BIP_CMAC_256, SKW_CIPHER_SUITE_BIP_GMAC_128, SKW_CIPHER_SUITE_BIP_GMAC_256, SKW_CIPHER_SUITE_SMS4, }; #ifdef CONFIG_SWT6621S_WDS static int skw_set_wds_mib(struct wiphy *wiphy, bool enable) { return skw_util_set_mib_enable(wiphy, 0, SKW_MIB_SET_WDS_ENABLE, enable); } #endif static int skw_set_once_noa_mib(struct wiphy *wiphy, u8 en, u8 pre_time, u8 abs_time) { int ret; u16 *plen; struct skw_tlv_conf conf; struct skw_once_noa_enable_mib once_noa; skw_dbg("once_noa: %d %d %d\n", en, pre_time, abs_time); once_noa.en = en; once_noa.go_pre_time = pre_time; once_noa.go_abs_time = abs_time; ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) { skw_err("alloc failed\n"); return ret; } plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_err("reserve failed\n"); skw_tlv_free(&conf); return -ENOMEM; } if (skw_tlv_add(&conf, SKW_MIB_SET_ONCE_NOA_ENABLE, &once_noa, sizeof(once_noa))) { skw_err("set once noa enable failed\n"); skw_tlv_free(&conf); return -EINVAL; } *plen = conf.total_len; ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_warn("failed, ret: %d\n", ret); skw_tlv_free(&conf); return ret; } static int skw_set_ratio_level_mib(struct wiphy *wiphy, u8 valid, u8 level) { int ret; u16 *plen; struct skw_tlv_conf conf; struct skw_noa_ratio_mib ratio; skw_dbg("ratio level: %d %d\n", valid, level); ratio.valid = valid; ratio.ratio_lv = level; ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) { skw_err("alloc failed\n"); return ret; } plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_err("reserve failed\n"); skw_tlv_free(&conf); return -ENOMEM; } if (skw_tlv_add(&conf, SKW_MIB_SET_NOA_RATIO_TYPE, &ratio, sizeof(ratio))) { skw_err("set ratio level failed\n"); skw_tlv_free(&conf); return -EINVAL; } *plen = conf.total_len; ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_warn("failed, ret: %d\n", ret); skw_tlv_free(&conf); return ret; } int skw_set_dot11k_mib(struct wiphy *wiphy, struct net_device *dev, bool enable) { struct skw_iface *iface; if (!dev) { skw_err("dev is null\n"); return -EINVAL; } iface = netdev_priv(dev); return skw_util_set_mib_enable(wiphy, iface->id, SKW_MIB_SET_11K_TLV_ID, enable); } int skw_set_dot11v_mib(struct wiphy *wiphy, struct net_device *dev, bool enable) { struct skw_iface *iface; if (!dev) { skw_err("dev is null\n"); return -EINVAL; } iface = netdev_priv(dev); return skw_util_set_mib_enable(wiphy, iface->id, SKW_MIB_SET_11V_TLV_ID, enable); } int skw_set_dot11r_mib(struct wiphy *wiphy, struct net_device *dev, bool enable) { struct skw_iface *iface; if (!dev) { skw_err("dev is null\n"); return -EINVAL; } iface = netdev_priv(dev); return skw_util_set_mib_enable(wiphy, iface->id, SKW_MIB_SET_11R_TLV_ID, enable); } int skw_set_5ghz_band_mib(struct wiphy *wiphy, u32 band) { int ret; u16 *plen; struct skw_tlv_conf conf; skw_dbg("5ghz_band: %d\n", band); ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) { skw_err("alloc failed\n"); return ret; } plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_err("reserve failed\n"); skw_tlv_free(&conf); return -ENOMEM; } if (skw_tlv_add(&conf, SKW_MIB_SET_BAND_5G, &band, sizeof(band))) { skw_err("set 5ghz band failed\n"); skw_tlv_free(&conf); return -EINVAL; } *plen = conf.total_len; ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_warn("failed, ret: %d\n", ret); skw_tlv_free(&conf); return ret; } static inline void skw_iftype_dump(int iftype_num[NUM_NL80211_IFTYPES]) { int i; for (i = 0; i < NUM_NL80211_IFTYPES; i++) { if (iftype_num[i]) skw_info("%s: %d\n", skw_iftype_name(i), iftype_num[i]); } } static void skw_count_iftype(struct wiphy *wiphy, int num[NUM_NL80211_IFTYPES]) { int i; struct skw_iface *iface; struct skw_core *skw = wiphy_priv(wiphy); spin_lock_bh(&skw->vif.lock); for (i = 0; i < SKW_NR_IFACE; i++) { iface = skw->vif.iface[i]; if (!iface || (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) || (iface->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE)) continue; num[iface->wdev.iftype]++; } spin_unlock_bh(&skw->vif.lock); } static struct wireless_dev *skw_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { int ret; struct skw_iface *iface; u8 vif_id = SKW_INVALID_ID; int iftype_num[NUM_NL80211_IFTYPES] = {0}; skw_dbg("%s(%s), mac: %pM\n", name, skw_iftype_name(type), params->macaddr); skw_count_iftype(wiphy, iftype_num); ret = skw_compat_check_combs(wiphy, 0, 0, iftype_num); if (ret) { skw_err("check combinations failed, %s(%s)\n", name, skw_iftype_name(type)); skw_iftype_dump(iftype_num); return ERR_PTR(-EINVAL); } if (type == NL80211_IFTYPE_P2P_DEVICE) vif_id = SKW_LAST_IFACE_ID; iface = skw_add_iface(wiphy, name, type, params->macaddr, vif_id, type != NL80211_IFTYPE_P2P_DEVICE); if (IS_ERR(iface)) { skw_err("failed, %ld\n", PTR_ERR(iface)); return ERR_CAST(iface); } return &iface->wdev; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) static struct wireless_dev *skw_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, struct vif_params *params) { u32 flags = 0; return skw_add_virtual_intf(wiphy, name, name_assign_type, type, &flags, params); } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) static struct wireless_dev *skw_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { return skw_add_virtual_intf(wiphy, name, name_assign_type, type, flags, params); } #else static struct wireless_dev *skw_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { return skw_add_virtual_intf(wiphy, name, 0, type, flags, params); } #endif static int skw_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) { struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); skw_dbg("iftype: %d, iface id: %d\n", wdev->iftype, iface->id); return skw_del_iface(wiphy, iface); } static int skw_change_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { u8 *mac; int ret; int iftype_num[NUM_NL80211_IFTYPES] = {0}; struct skw_iface *iface = netdev_priv(dev); skw_dbg("%s (inst: %d), %s -> %s, mac: %pM, 4addr: %d, flags: 0x%x\n", netdev_name(dev), iface->id, skw_iftype_name(dev->ieee80211_ptr->iftype), skw_iftype_name(type), params->macaddr, params->use_4addr, iface->flags); if (iface->flags & SKW_IFACE_FLAG_LEGACY_P2P_DEV) iface->wdev.iftype = type; if (iface->wdev.iftype == type) return 0; skw_count_iftype(wiphy, iftype_num); iftype_num[type]++; iftype_num[iface->wdev.iftype]--; ret = skw_compat_check_combs(wiphy, 0, 0, iftype_num); if (ret) { skw_err("check combinations failed, %s(inst: %d), %s -> %s\n", netdev_name(dev), iface->id, skw_iftype_name(dev->ieee80211_ptr->iftype), skw_iftype_name(type)); skw_iftype_dump(iftype_num); return ret; } if (iface->ndev) netif_tx_stop_all_queues(dev); ret = skw_iface_teardown(wiphy, iface); if (ret) { skw_err("teardown failed, %s (inst: %d), ret: %d\n", skw_iftype_name(iface->wdev.iftype), iface->id, ret); goto out; } #ifdef CONFIG_SWT6621S_WDS skw_set_wds_mib(wiphy, params->use_4addr); #endif if (is_valid_ether_addr(params->macaddr)) mac = params->macaddr; else mac = (u8 *)wdev_address(dev->ieee80211_ptr); ret = skw_iface_setup(wiphy, dev, iface, mac, type, iface->id); if (ret) { skw_err("open dev failed, %s (inst: %d)\n", skw_iftype_name(type), iface->id); ret = skw_iface_setup(wiphy, dev, iface, iface->addr, iface->wdev.iftype, iface->id); if (ret) skw_err("open dev failed, %s (inst: %d)\n", skw_iftype_name(iface->wdev.iftype), iface->id); } out: if (iface->ndev) netif_tx_start_all_queues(dev); return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) static int skw_cfg80211_change_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, struct vif_params *params) { u32 flags = 0; return skw_change_intf(wiphy, dev, type, &flags, params); } #else static int skw_cfg80211_change_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { return skw_change_intf(wiphy, dev, type, flags, params); } #endif static int skw_get_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *params)) { skw_dbg("dev: %s, link: %d, key_idx: %d, pairwise: %d, mac: %pM\n", netdev_name(dev), link_id, key_idx, pairwise, mac_addr); return 0; } static int skw_cmd_add_key(struct wiphy *wiphy, struct net_device *dev, int cipher, u8 key_idx, int key_type, const u8 *key, int key_len, const u8 *addr) { struct skw_key_params params; struct skw_iface *iface = netdev_priv(dev); u8 wapi_tx_pn[] = {0x36, 0x5c, 0x36, 0x5c, 0x36, 0x5c}; memset(¶ms, 0x0, sizeof(params)); if (addr) skw_ether_copy(params.mac_addr, addr); else memset(params.mac_addr, 0xff, ETH_ALEN); memcpy(params.key, key, key_len); params.key_type = key_type; params.key_len = key_len; params.key_id = key_idx; params.cipher_type = cipher; params.pn[0] = 1; switch (cipher) { case SKW_CIPHER_TYPE_SMS4: memcpy(params.pn, wapi_tx_pn, SKW_PN_LEN); if (is_skw_ap_mode(iface)) params.pn[0] += 1; break; case SKW_CIPHER_TYPE_TKIP: if (is_skw_ap_mode(iface)) memcpy(¶ms.key[0], key, 32); else { memcpy(¶ms.key[0], key, 16); memcpy(¶ms.key[16], key + 24, 8); memcpy(¶ms.key[24], key + 16, 8); } break; default: break; } if (is_skw_ap_mode(iface) && iface->buf_keys_idx < SKW_MAX_BUF_KEYS && !SKW_TEST(iface->flags, SKW_IFACE_FLAG_AP_STARTED)) { memcpy(&iface->buf_keys[iface->buf_keys_idx++], ¶ms, sizeof(params)); SKW_SET(iface->flags, SKW_IFACE_FLAG_BUF_KEY); skw_dbg("lead key buffered, idx: %d\n", iface->buf_keys_idx - 1); return 0; } else return skw_send_msg(wiphy, dev, SKW_CMD_ADD_KEY, ¶ms, sizeof(params), NULL, 0); } static int skw_set_key(struct wiphy *wiphy, struct net_device *dev, struct skw_key_conf *conf, u8 key_idx, int key_type, const u8 *addr, struct key_params *params) { int i, cipher, ret; struct skw_key *key, *old_key; cipher = to_skw_cipher_type(params->cipher); if (cipher == SKW_CIPHER_TYPE_INVALID) { skw_warn("cipher 0x%x unsupported\n", params->cipher); return -ENOTSUPP; } key = SKW_ZALLOC(sizeof(struct skw_key), GFP_KERNEL); if (!key) return -ENOMEM; key->key_len = params->key_len; memcpy(key->key_data, params->key, params->key_len); if (params->seq) { skw_hex_dump("seq", params->seq, params->seq_len, false); for (i = 1; i < IEEE80211_NUM_TIDS; i++) memcpy(key->rx_pn[i], params->seq, SKW_PN_LEN); } conf->skw_cipher = cipher; old_key = rcu_dereference_protected(conf->key[key_idx], lockdep_is_held(&conf->lock)); rcu_assign_pointer(conf->key[key_idx], key); SKW_SET(conf->installed_bitmap, BIT(key_idx)); if (old_key) kfree_rcu(old_key, rcu); if (cipher == SKW_CIPHER_TYPE_WEP40 || cipher == SKW_CIPHER_TYPE_WEP104) { SKW_SET(conf->flags, SKW_KEY_FLAG_WEP_SHARE); return 0; } ret = skw_cmd_add_key(wiphy, dev, cipher, key_idx, key_type, params->key, params->key_len, addr); if (ret) { RCU_INIT_POINTER(conf->key[key_idx], NULL); SKW_CLEAR(conf->installed_bitmap, BIT(key_idx)); kfree_rcu(key, rcu); } return ret; } static int skw_add_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *addr, struct key_params *params) { const u8 *mac; int ret, key_type; struct skw_key_conf *conf; struct skw_peer_ctx *ctx; struct skw_iface *iface = netdev_priv(dev); skw_dbg("%s, key_idx: %d, cipher: 0x%x, pairwise: %d, mac: %pM\n", netdev_name(dev), key_idx, params->cipher, pairwise, addr); key_type = pairwise ? SKW_KEY_TYPE_PTK : to_skw_gtk(key_idx); if (addr) { ctx = skw_peer_ctx(iface, addr); if (!ctx) { skw_warn("%pM not linked\n", addr); return -ENOLINK; } skw_peer_ctx_lock(ctx); if (!ctx->peer) { skw_peer_ctx_unlock(ctx); return 0; } if (pairwise) conf = &ctx->peer->ptk_conf; else conf = &ctx->peer->gtk_conf; mutex_lock(&conf->lock); ret = skw_set_key(wiphy, dev, conf, key_idx, key_type, addr, params); mutex_unlock(&conf->lock); skw_peer_ctx_unlock(ctx); } else { if (is_skw_ap_mode(iface)) mac = NULL; else mac = iface->sta.core.bss.bssid; conf = &iface->key_conf; mutex_lock(&conf->lock); ret = skw_set_key(wiphy, dev, conf, key_idx, key_type, mac, params); mutex_unlock(&conf->lock); } if (ret) skw_err("failed, cipher: 0x%x, ptk: %d, idx: %d, ret: %d\n", params->cipher, pairwise, key_idx, ret); return ret; } static int skw_cmd_del_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, int key_type, int cipher, const u8 *addr) { struct skw_key_params params; memset(¶ms, 0x0, sizeof(params)); if (addr) skw_ether_copy(params.mac_addr, addr); else memset(params.mac_addr, 0xff, ETH_ALEN); params.key_type = key_type; params.cipher_type = cipher; params.key_id = key_idx; return skw_send_msg(wiphy, dev, SKW_CMD_DEL_KEY, ¶ms, sizeof(params), NULL, 0); } static int skw_remove_key(struct wiphy *wiphy, struct net_device *dev, struct skw_key_conf *conf, u8 key_idx, int key_type, const u8 *addr) { int ret; struct skw_key *key; if (SKW_TEST(conf->installed_bitmap, BIT(key_idx))) { ret = skw_cmd_del_key(wiphy, dev, key_idx, key_type, conf->skw_cipher, addr); if (ret) skw_err("failed, ret: %d\n", ret); } key = rcu_dereference_protected(conf->key[key_idx], lockdep_is_held(&conf->lock)); RCU_INIT_POINTER(conf->key[key_idx], NULL); SKW_CLEAR(conf->installed_bitmap, BIT(key_idx)); if (SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_SHARE)) { SKW_CLEAR(conf->flags, SKW_KEY_FLAG_WEP_SHARE); SKW_CLEAR(conf->flags, SKW_KEY_FLAG_WEP_UNICAST); SKW_CLEAR(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST); } if (key) kfree_rcu(key, rcu); return 0; } static int skw_del_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *addr) { int ret, key_type; struct skw_key_conf *conf; const u8 *mac = NULL; struct skw_peer_ctx *ctx = NULL; struct skw_iface *iface = netdev_priv(dev); skw_dbg("link: %d, key_idx: %d, pairwise: %d, mac: %pM\n", link_id, key_idx, pairwise, addr); if (key_idx >= SKW_NUM_MAX_KEY) { skw_err("key index %d out of bounds\n", key_idx); return -EINVAL; } key_type = pairwise ? SKW_KEY_TYPE_PTK : to_skw_gtk(key_idx); if (addr) { ctx = skw_peer_ctx(iface, addr); if (!ctx) return 0; skw_peer_ctx_lock(ctx); if (!ctx->peer) { skw_peer_ctx_unlock(ctx); return 0; } if (pairwise) conf = &ctx->peer->ptk_conf; else conf = &ctx->peer->gtk_conf; mutex_lock(&conf->lock); ret = skw_remove_key(wiphy, dev, conf, key_idx, key_type, addr); mutex_unlock(&conf->lock); skw_peer_ctx_unlock(ctx); } else { conf = &iface->key_conf; if (is_skw_sta_mode(iface)) mac = iface->sta.core.bss.bssid; mutex_lock(&conf->lock); ret = skw_remove_key(wiphy, dev, conf, key_idx, key_type, mac); mutex_unlock(&conf->lock); } return ret; } /* for WEP keys */ static int skw_set_default_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool unicast, bool multicast) { int ret = 0, key_len; struct skw_key *key; const u8 *mac = NULL; u8 key_data[WLAN_MAX_KEY_LEN] = {0}; struct skw_iface *iface = netdev_priv(dev); struct skw_key_conf *conf = &iface->key_conf; skw_dbg("dev: %s, link: %d, key_idx: %d, unicast: %d, multicast: %d\n", netdev_name(dev), link_id, key_idx, unicast, multicast); if (!(conf->installed_bitmap & BIT(key_idx))) return 0; if (is_skw_sta_mode(iface)) mac = iface->sta.core.bss.bssid; rcu_read_lock(); key = conf->key[key_idx]; if (key) { memcpy(key_data, key->key_data, key->key_len); key_len = key->key_len; } rcu_read_unlock(); if (!key) return 0; conf->wep_idx = key_idx; if (unicast) { ret = skw_cmd_add_key(wiphy, dev, conf->skw_cipher, key_idx, SKW_KEY_TYPE_PTK, key_data, key_len, mac); if (ret) SKW_SET(conf->flags, SKW_KEY_FLAG_WEP_UNICAST); } if (multicast) { ret = skw_cmd_add_key(wiphy, dev, conf->skw_cipher, key_idx, SKW_KEY_TYPE_GTK, key_data, key_len, mac); if (ret) SKW_SET(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST); } return 0; } /* for 11w */ static int skw_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_index) { skw_dbg("%s, key index: %d\n", netdev_name(dev), key_index); return 0; } static int skw_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_acl_data *acl) { int size; struct skw_iface *iface = netdev_priv(dev); if (!acl) return 0; skw_dbg("dev: %s, nr_entries: %d\n", netdev_name(dev), acl->n_acl_entries); if (!acl->n_acl_entries) { SKW_KFREE(iface->sap.acl); return 0; } size = acl->n_acl_entries * sizeof(struct mac_address); size += sizeof(struct cfg80211_acl_data); SKW_KFREE(iface->sap.acl); iface->sap.acl = SKW_ZALLOC(size, GFP_KERNEL); if (!iface->sap.acl) return -ENOMEM; memcpy(iface->sap.acl, acl, size); skw_queue_work(wiphy, netdev_priv(dev), SKW_WORK_ACL_CHECK, NULL, 0); return 0; } static bool skw_channel_allowed(struct wiphy *wiphy, u16 channel) { #define BITMAP_SIZE ((164 + BITS_PER_LONG) / BITS_PER_LONG) int i, nr_channel; struct skw_iface *iface; bool extra_chn = false; struct skw_core *skw = wiphy_priv(wiphy); int iftype_num[NUM_NL80211_IFTYPES] = {0}; long channel_map[BITMAP_SIZE] = {0}; spin_lock_bh(&skw->vif.lock); for (nr_channel = 0, i = 0; i < SKW_NR_IFACE; i++) { struct ieee80211_channel *chan = NULL; iface = skw->vif.iface[i]; if (!iface) continue; switch (iface->wdev.iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: chan = iface->sap.cfg.channel; break; case NL80211_IFTYPE_STATION: if (atomic_read(&iface->actived_ctx) > 1) extra_chn = true; /* fall through */ skw_fallthrough; case NL80211_IFTYPE_P2P_CLIENT: chan = iface->sta.core.bss.channel; break; default: break; } if (chan && !test_and_set_bit(chan->hw_value, channel_map)) nr_channel++; } spin_unlock_bh(&skw->vif.lock); for (i = 0; extra_chn && (i < SKW_MAX_PEER_SUPPORT); i++) { struct skw_peer_ctx *ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[i]; skw_peer_ctx_lock(ctx); if (ctx->peer && ctx->peer->channel && !test_and_set_bit(ctx->peer->channel, channel_map)) nr_channel++; skw_peer_ctx_unlock(ctx); } if (!test_bit(channel, channel_map)) nr_channel++; if (!skw_compat_check_combs(wiphy, nr_channel, 0, iftype_num)) return true; skw_err("channel %d not allowed, total:%d\n", channel, nr_channel); skw_hex_dump("channels", channel_map, sizeof(channel_map), true); return false; } int skw_sap_set_mib(struct wiphy *wiphy, struct net_device *dev, const u8 *ies, int ies_len) { int ret = 0; u16 *plen; struct skw_tlv_conf conf; struct skw_iface *iface = netdev_priv(dev); u32 val_zero = 0; u32 val_one = 1; ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL); if (ret) return ret; plen = skw_tlv_reserve(&conf, 2); if (iface->extend.wireless_mode) { skw_dbg("wireless_mode: %d\n", iface->extend.wireless_mode); switch (iface->extend.wireless_mode) { case SKW_WIRELESS_11G_ONLY: if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HT, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_B, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_A, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_G, &val_one, 4)) { skw_err("set 11G mode failed\n"); skw_tlv_free(&conf); } break; case SKW_WIRELESS_11N_ONLY: if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HT, &val_one, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_B, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_A, &val_zero, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_G, &val_zero, 4)) { skw_err("set 11N mode failed\n"); skw_tlv_free(&conf); } break; default: break; } } else if (ies && ies_len) { u8 ext_eid; const u8 *ie; int ht_enable = 0, vht_enable = 0, he_enable = 0; ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); if (ie && ie[1]) ht_enable = 1; ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, ies, ies_len); if (ie && ie[1]) vht_enable = 1; ext_eid = SKW_WLAN_EID_EXT_HE_CAPABILITY; ie = skw_find_ie_match(SKW_WLAN_EID_EXTENSION, ies, ies_len, &ext_eid, 1, 2); if (ie && ie[1]) he_enable = 1; skw_dbg("ht_enable: %d, vht_enable: %d, he_enable: %d\n", ht_enable, vht_enable, he_enable); if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HT, &ht_enable, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &vht_enable, 4) || skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &he_enable, 4)) { skw_err("set beacon capa failed\n"); skw_tlv_free(&conf); } } if (conf.total_len) { *plen = conf.total_len; ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_err("failed, ret: %d\n", ret); } skw_tlv_free(&conf); return ret; } #ifdef CONFIG_SWT6621S_USB3_WORKAROUND static int skw_switch_usb3_to_usb2_using_2G(struct skw_iface *iface, enum nl80211_band band) { struct skw_core *skw = iface->skw; char mode[20]; int ret = 0; skw_dbg("bus:%d align_value:%d band:%d\n", skw->hw.bus, skw->hw_pdata->align_value, band); if (skw->hw.bus == SKW_BUS_USB && skw->hw_pdata->align_value == 1024 && band == NL80211_BAND_2GHZ && skw->hw_pdata->usb_speed_switch) { if (test_bit(SKW_FLAG_SWITCHING_USB_MODE, &skw->flags)) { skw_dbg("already in switching\n"); return -EBUSY; } set_bit(SKW_FLAG_SWITCHING_USB_MODE, &skw->flags); skw->hw_pdata->usb_speed_switch(mode); skw_dbg("change usb mode to %s\n", mode); skw_dbg("waiting for the switch completion"); if (wait_for_completion_interruptible_timeout(&skw->usb_switch_done, SKW_RECOVERY_TIMEOUT) == 0) { skw_err("switch timeout\n"); ret = -ETIME; } } return ret; } #endif static int skw_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *settings) { int ret, bw; int total, fixed, offset = 0; struct skw_startap_resp resp = {}; struct skw_startap_param *param = NULL; struct skw_iface *iface = netdev_priv(dev); struct skw_core *skw = wiphy_priv(wiphy); struct cfg80211_beacon_data *bcn = &settings->beacon; struct cfg80211_chan_def *chandef = &settings->chandef; struct skw_key_conf *conf = &iface->key_conf; skw_info("ndev: %s\n", netdev_name(dev)); skw_dbg(" * ssid: %s\n", settings->ssid); skw_dbg(" * bssid: %pM\n", iface->addr); skw_dbg(" * channel: %d band:%d (BW: %d)\n", chandef->chan->hw_value, chandef->chan->band, chandef->width); skw_dbg(" * auth type: %d\n", settings->auth_type); skw_dbg(" * akm_suites: %d\n", settings->crypto.n_akm_suites); if (!skw_channel_allowed(wiphy, chandef->chan->hw_value)) return -ENOTSUPP; bw = to_skw_bw(settings->chandef.width); if (bw == SKW_CHAN_WIDTH_MAX) { skw_err("BW %d not support\n", settings->chandef.width); return -ENOTSUPP; } #ifdef CONFIG_SWT6621S_USB3_WORKAROUND ret = skw_switch_usb3_to_usb2_using_2G(iface, chandef->chan->band); if (ret) return ret; #endif skw_sap_set_mib(wiphy, dev, bcn->tail, bcn->tail_len); fixed = sizeof(struct skw_startap_param); total = fixed + bcn->head_len + bcn->tail_len + bcn->probe_resp_len; param = SKW_ZALLOC(total, GFP_KERNEL); if (!param) { skw_err("malloc failed, size: %d\n", total); return -ENOMEM; } param->chan_width = bw; param->chan = chandef->chan->hw_value; param->band = to_skw_band(chandef->chan->band); param->center_chn1 = skw_freq_to_chn(chandef->center_freq1); param->center_chn2 = skw_freq_to_chn(chandef->center_freq2); param->beacon_int = settings->beacon_interval; param->dtim_period = settings->dtim_period; param->ssid_len = settings->ssid_len; memcpy(param->ssid, settings->ssid, settings->ssid_len); if (settings->hidden_ssid) param->flags |= settings->hidden_ssid; if (bcn->head) { skw_hex_dump("beacon_head", bcn->head, bcn->head_len, false); param->beacon_head_len = bcn->head_len; param->beacon_head_offset = offset + fixed; memcpy(param->ies + offset, bcn->head, bcn->head_len); offset += bcn->head_len; } if (bcn->tail) { skw_hex_dump("beacon_tail", bcn->tail, bcn->tail_len, false); param->beacon_tail_offset = offset + fixed; param->beacon_tail_len = bcn->tail_len; memcpy(param->ies + offset, bcn->tail, bcn->tail_len); offset += bcn->tail_len; skw_iface_set_wmm_capa(iface, bcn->tail, bcn->tail_len); } if (bcn->probe_resp) { skw_hex_dump("probe_resp", bcn->probe_resp, bcn->probe_resp_len, false); param->probe_rsp_ies_offset = offset + fixed; param->probe_rsp_ies_len = bcn->probe_resp_len; memcpy(param->ies + offset, bcn->probe_resp, bcn->probe_resp_len); offset += bcn->probe_resp_len; if (iface->sap.probe_resp) { memcpy(iface->sap.probe_resp, bcn->probe_resp, bcn->probe_resp_len); iface->sap.probe_resp_len = bcn->probe_resp_len; } } if (skw_recovery_data_update(iface, param, total)) { skw_err("build recovery failed\n"); SKW_KFREE(param); return -ENOMEM; } skw_edma_mask_irq(skw, iface->lmac_id); ret = skw_send_msg(wiphy, dev, SKW_CMD_START_AP, param, total, &resp, sizeof(resp)); if (ret) { skw_err("failed, ret: %d\n", ret); skw_recovery_data_clear(iface); SKW_KFREE(param); return ret; } skw_startap_resp_handler(skw, iface, &resp); if (SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_UNICAST) || SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST)) skw_set_default_key(wiphy, dev, 0, conf->wep_idx, SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_UNICAST), SKW_TEST(conf->flags, SKW_KEY_FLAG_WEP_MULTICAST)); if (skw_compat_cfg80211_chandef_dfs_required(wiphy, chandef, iface->wdev.iftype)) { ret = skw_dfs_chan_init(wiphy, dev, chandef, 0); if (ret) { skw_err("dfs channel init failed, ret: %d\n", ret); SKW_KFREE(param); return ret; } ret = skw_dfs_start_monitor(wiphy, dev); if (ret) { skw_dbg("start dfs monitor failed, ret: %d\n", ret); SKW_KFREE(param); return ret; } } skw_dpd_set_coeff_params(wiphy, dev, param->chan, param->center_chn1, param->center_chn2, param->chan_width); skw_set_mac_acl(wiphy, dev, settings->acl); skw_set_ratio_level_mib(wiphy, skw->config.fw.noa_ratio_en, skw->config.fw.noa_ratio_idx); skw_set_once_noa_mib(wiphy, skw->config.fw.once_noa_en, skw->config.fw.once_noa_pre, skw->config.fw.once_noa_abs); memcpy(iface->sap.cfg.ssid, settings->ssid, settings->ssid_len); iface->sap.cfg.ssid_len = settings->ssid_len; iface->sap.cfg.auth_type = settings->auth_type; iface->sap.cfg.channel = chandef->chan; iface->sap.cfg.width = bw; skw_ether_copy(iface->sap.cfg.bssid, iface->addr); memcpy(&iface->sap.cfg.crypto, &settings->crypto, sizeof(settings->crypto)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) iface->sap.cfg.ht_cap = SKW_KMEMDUP(settings->ht_cap, sizeof(*settings->ht_cap), GFP_KERNEL); iface->sap.cfg.vht_cap = SKW_KMEMDUP(settings->vht_cap, sizeof(*settings->vht_cap), GFP_KERNEL); iface->sap.ht_required = settings->ht_required; iface->sap.vht_required = settings->vht_required; iface->sap.cfg.crypto.wep_keys = NULL; iface->sap.cfg.crypto.psk = NULL; #else iface->sap.cfg.ht_cap = NULL; iface->sap.cfg.vht_cap = NULL; iface->sap.ht_required = false; iface->sap.vht_required = false; #endif if (iface->skw->hw.bus == SKW_BUS_PCIE) { if (skw_edma_get_refill((void *)iface->skw, iface->lmac_id) == 0) skw_edma_init_data_chan((void *)iface->skw, iface->lmac_id); else skw_edma_inc_refill((void *)iface->skw, iface->lmac_id); } SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); netif_carrier_on(dev); if (SKW_TEST(iface->flags, SKW_IFACE_FLAG_BUF_KEY)) { int i; for (i = 0; i < iface->buf_keys_idx; i++) { skw_send_msg(wiphy, dev, SKW_CMD_ADD_KEY, &iface->buf_keys[i], sizeof(struct skw_key_params), NULL, 0); skw_dbg("send key buffered, idx: %d\n", i); memset(&iface->buf_keys[i], 0, sizeof(struct skw_key_params)); } iface->buf_keys_idx = 0; SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_BUF_KEY); } SKW_SET(iface->flags, SKW_IFACE_FLAG_AP_STARTED); SKW_KFREE(param); return 0; } static int skw_sap_del_sta(struct wiphy *wiphy, struct net_device *dev, struct skw_peer_ctx *ctx, u8 subtype, u16 reason) { int ret; bool tx = true; const u8 *mac = NULL; struct skw_iface *iface = netdev_priv(dev); if (!ctx) return 0; skw_peer_ctx_lock(ctx); if (ctx->peer) { mac = ctx->peer->addr; __skw_peer_ctx_transmit(ctx, false); skw_set_state(&ctx->peer->sm, SKW_STATE_NONE); tx = !(ctx->peer->flags & SKW_PEER_FLAG_DEAUTHED); SKW_SET(ctx->peer->flags, SKW_PEER_FLAG_DEAUTHED); } skw_peer_ctx_unlock(ctx); if (!mac) return 0; skw_mlme_ap_remove_client(iface, mac); ret = skw_cmd_del_sta(wiphy, dev, mac, subtype, reason, tx); if (!ret) skw_peer_ctx_bind(iface, ctx, NULL); return ret; } static void skw_sap_flush_sta(struct wiphy *wiphy, struct skw_iface *iface, u8 subtype, u16 reason) { int idx; struct skw_peer_ctx *ctx; struct skw_core *skw = wiphy_priv(wiphy); u32 peer_map = atomic_read(&iface->peer_map); while (peer_map) { idx = ffs(peer_map) - 1; SKW_CLEAR(peer_map, BIT(idx)); ctx = &skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; if (!ctx) continue; skw_sap_del_sta(wiphy, iface->ndev, ctx, subtype, reason); } } static int skw_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id) { int ret = 0; struct skw_iface *iface = netdev_priv(dev); skw_info("ndev: %s, link id: %d\n", netdev_name(dev), link_id); if (iface->skw->hw.bus == SKW_BUS_PCIE) skw_edma_dec_refill((void *)iface->skw, iface->lmac_id); netif_carrier_off(dev); skw_sap_flush_sta(wiphy, iface, 12, SKW_LEAVE); // set flag for tx thread to filter out skb in tx cache // mutex_lock(&skw->txrx.lock); // SKW_CLEAR(skw->txrx.tx_map, BIT(iface->id)); // mutex_unlock(&skw->txrx.lock); // WARN_ON(iface->sta_list.count); skw_purge_key_conf(&iface->key_conf); skw_recovery_data_clear(iface); SKW_SET(iface->flags, SKW_IFACE_FLAG_DEAUTH); skw_dfs_deinit(wiphy, dev); ret = skw_send_msg(wiphy, dev, SKW_CMD_STOP_AP, NULL, 0, NULL, 0); if (ret) { SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); skw_err("failed, ret = %d\n", ret); return ret; } SKW_KFREE(iface->sap.acl); SKW_KFREE(iface->sap.cfg.ht_cap); SKW_KFREE(iface->sap.cfg.vht_cap); SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_AP_STARTED); skw_lmac_unbind_iface(wiphy_priv(wiphy), iface->lmac_id, iface->id); return 0; } static int skw_change_beacon(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_beacon_data *bcn) { int ret = -1; int total, fixed, offset = 0; struct skw_iface *iface = netdev_priv(dev); struct skw_beacon_params *param = NULL; skw_dbg("dev: %s\n", netdev_name(dev)); if (!bcn) return -EINVAL; fixed = sizeof(struct skw_beacon_params); total = fixed + bcn->head_len + bcn->tail_len + bcn->probe_resp_len; param = SKW_ZALLOC(total, GFP_KERNEL); if (!param) { skw_err("malloc failed, size: %d\n", total); return -ENOMEM; } if (bcn->head) { skw_hex_dump("beacon_head", bcn->head, bcn->head_len, false); param->beacon_head_len = bcn->head_len; param->beacon_head_offset = fixed + offset; memcpy(param->ies + offset, bcn->head, bcn->head_len); offset += bcn->head_len; } if (bcn->tail) { skw_hex_dump("beacon_tail", bcn->tail, bcn->tail_len, false); param->beacon_tail_offset = fixed + offset; param->beacon_tail_len = bcn->tail_len; memcpy(param->ies + offset, bcn->tail, bcn->tail_len); offset += bcn->tail_len; } if (bcn->probe_resp) { skw_hex_dump("probe_resp", bcn->probe_resp, bcn->probe_resp_len, false); param->probe_rsp_offset = fixed + offset; param->probe_rsp_len = bcn->probe_resp_len; memcpy(param->ies + offset, bcn->probe_resp, bcn->probe_resp_len); offset += bcn->probe_resp_len; if (iface->sap.probe_resp) { memcpy(iface->sap.probe_resp, bcn->probe_resp, bcn->probe_resp_len); iface->sap.probe_resp_len = bcn->probe_resp_len; } } ret = skw_send_msg(wiphy, dev, SKW_CMD_CHANGE_BEACON, param, total, NULL, 0); if (ret) skw_err("failed, ret: %d\n", ret); SKW_KFREE(param); return ret; } void skw_set_state(struct skw_sm *sm, enum SKW_STATES state) { skw_log(SKW_STATE, "[%s] inst: %d, %s -> %pM, state: %s -> %s\n", SKW_TAG_STATE, sm->inst, skw_iftype_name(sm->iface_iftype), sm->addr, skw_state_name(sm->state), skw_state_name(state)); sm->state = state; } int skw_change_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_parameters *params) { struct skw_iface *iface = netdev_priv(dev); u32 flags_set = params->sta_flags_set; struct skw_peer_ctx *ctx = NULL; skw_dbg("%s(%s), mac: %pM, flags_set: 0x%x\n", netdev_name(dev), skw_iftype_name(dev->ieee80211_ptr->iftype), mac, params->sta_flags_set); ctx = skw_peer_ctx(iface, mac); if (!ctx) return -EINVAL; skw_peer_ctx_lock(ctx); switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_ADHOC: if (flags_set & BIT(NL80211_STA_FLAG_ASSOCIATED)) { __skw_peer_ctx_transmit(ctx, true); skw_set_state(&ctx->peer->sm, SKW_STATE_ASSOCED); if (iface->sap.cfg.crypto.n_akm_suites == 0) flags_set |= BIT(NL80211_STA_FLAG_AUTHORIZED); } if (flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { skw_set_state(&ctx->peer->sm, SKW_STATE_COMPLETED); atomic_set(&ctx->peer->rx_filter, SKW_RX_FILTER_NONE); } break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { skw_set_state(&iface->sta.core.sm, SKW_STATE_COMPLETED); atomic_set(&ctx->peer->rx_filter, SKW_RX_FILTER_NONE); skw_set_ip_to_fw(wiphy, dev); } break; default: break; } skw_peer_ctx_unlock(ctx); return 0; } static int skw_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) const u8 *mac, #else u8 *mac, #endif struct station_parameters *params) { return skw_change_station(wiphy, dev, (const u8 *)mac, params); } static int skw_set_sta_wep_key(struct wiphy *wiphy, struct skw_iface *iface, const u8 *mac, enum SKW_KEY_TYPE key_type) { int idx; struct skw_key_params key_params; struct skw_key *key; struct skw_key_conf *conf = &iface->key_conf; skw_dbg("addr: %pM, key type: %d\n", mac, key_type); memset(&key_params, 0x0, sizeof(key_params)); idx = skw_key_idx(conf->installed_bitmap); if (idx == SKW_INVALID_ID) return -EINVAL; rcu_read_lock(); key = rcu_dereference(conf->key[idx]); rcu_read_unlock(); key_params.cipher_type = conf->skw_cipher; key_params.key_id = idx; key_params.key_len = key->key_len; key_params.key_type = key_type; memcpy(key_params.key, key->key_data, key->key_len); skw_ether_copy(key_params.mac_addr, mac); if (is_skw_ap_mode(iface) && iface->buf_keys_idx < SKW_MAX_BUF_KEYS && !SKW_TEST(iface->flags, SKW_IFACE_FLAG_AP_STARTED)) { memcpy(&iface->buf_keys[iface->buf_keys_idx++], &key_params, sizeof(key_params)); SKW_SET(iface->flags, SKW_IFACE_FLAG_BUF_KEY); skw_dbg("lead key buffered, idx: %d\n", iface->buf_keys_idx - 1); return 0; } else return skw_send_msg(wiphy, iface->ndev, SKW_CMD_ADD_KEY, &key_params, sizeof(key_params), NULL, 0); } int skw_cmd_del_sta(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, u8 type, u16 reason, bool tx_frame) { struct skw_del_sta_param params; skw_dbg("%s: addr: %pM, reason: %d, tx frame: %d\n", netdev_name(dev), mac, reason, tx_frame); params.reason_code = reason; skw_ether_copy(params.mac, mac); params.tx_frame = tx_frame; return skw_send_msg(wiphy, dev, SKW_CMD_DEL_STA, ¶ms, sizeof(params), NULL, 0); } int skw_del_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, u8 subtype, u16 reason) { struct skw_peer_ctx *ctx; struct skw_iface *iface = netdev_priv(dev); skw_info("subtype: %d, reason: %d, mac: %pM\n", subtype, reason, mac); if (!mac || is_broadcast_ether_addr(mac)) { skw_sap_flush_sta(wiphy, iface, subtype, reason); return 0; } ctx = skw_peer_ctx(iface, mac); if (!ctx) return -ENOENT; return skw_sap_del_sta(wiphy, dev, ctx, subtype, reason); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) static int skw_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, struct station_del_parameters *params) { return skw_del_station(wiphy, dev, params->mac, params->subtype, params->reason_code); } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) static int skw_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac) { return skw_del_station(wiphy, dev, mac, 12, /* Deauth */ WLAN_REASON_DEAUTH_LEAVING); } #else static int skw_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { return skw_del_station(wiphy, dev, (const u8 *)mac, 12, /* Deauth */ WLAN_REASON_DEAUTH_LEAVING); } #endif int skw_add_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_parameters *params) { struct skw_iface *iface = netdev_priv(dev); struct skw_peer_ctx *ctx; struct skw_peer *peer; int ret; u8 idx; skw_dbg("ndev: %s, mac: %pM, flags: 0x%x\n", netdev_name(dev), mac, params->sta_flags_set); ctx = skw_peer_ctx(iface, mac); if (!ctx) { peer = skw_peer_alloc(); if (!peer) { skw_err("failed, addr: %pM\n", mac); return -ENOMEM; } ret = skw_send_msg(wiphy, dev, SKW_CMD_ADD_STA, (void *)mac, ETH_ALEN, &idx, sizeof(idx)); if (ret) { skw_err("command failed, addr: %pM, ret: %d\n", mac, ret); SKW_KFREE(peer); return ret; } skw_peer_init(peer, mac, idx); ctx = skw_get_ctx(iface->skw, iface->lmac_id, idx); ret = skw_peer_ctx_bind(iface, ctx, peer); if (ret) { skw_cmd_del_sta(wiphy, dev, mac, 12, SKW_LEAVE, false); SKW_KFREE(peer); return -EINVAL; } } skw_peer_ctx_lock(ctx); __skw_peer_ctx_transmit(ctx, false); skw_set_state(&ctx->peer->sm, SKW_STATE_AUTHED); skw_peer_ctx_unlock(ctx); if (iface->key_conf.flags & SKW_KEY_FLAG_WEP_SHARE) skw_set_sta_wep_key(wiphy, iface, mac, SKW_KEY_TYPE_PTK); return 0; } static int skw_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) const u8 *mac, #else u8 *mac, #endif struct station_parameters *params) { return skw_add_station(wiphy, dev, (const u8 *)mac, params); } static void skw_set_rate_info(struct skw_rate *rate, struct rate_info *rinfo) { #if 0 skw_dbg("flags: %d, mcs: %d, bw: %d, gi: %d, nss: %d, he_ru: %d\n", rate->flags, rate->mcs_idx, rate->bw, rate->gi, rate->nss, rate->he_ru); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) switch (rate->bw) { case SKW_RATE_INFO_BW_40: rinfo->bw = RATE_INFO_BW_40; break; case SKW_RATE_INFO_BW_80: rinfo->bw = RATE_INFO_BW_80; break; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) case SKW_RATE_INFO_BW_HE_RU: rinfo->bw = RATE_INFO_BW_HE_RU; rinfo->he_ru_alloc = rate->he_ru; break; #endif default: rinfo->bw = RATE_INFO_BW_20; break; } #endif rinfo->flags = 0; switch (rate->flags) { case SKW_RATE_INFO_FLAGS_HT: rinfo->mcs = rate->mcs_idx; rinfo->flags |= RATE_INFO_FLAGS_MCS; if (rate->gi) rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; break; case SKW_RATE_INFO_FLAGS_VHT: rinfo->mcs = rate->mcs_idx; rinfo->nss = rate->nss; rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; if (rate->gi) rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; break; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) case SKW_RATE_INFO_FLAGS_HE: rate->gi = skw_gi_to_nl80211_info_gi(rate->gi); rinfo->mcs = rate->mcs_idx; rinfo->nss = rate->nss; rinfo->he_gi = rate->gi; rinfo->he_dcm = rate->he_dcm; rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; break; #endif default: rinfo->legacy = rate->legacy_rate; break; } } static int skw_get_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_info *sinfo) { u64 ts; int ret = -1; struct skw_peer_ctx *ctx; struct skw_station_params params = {0}; struct skw_get_sta_resp get_sta_resp; struct skw_iface *iface = netdev_priv(dev); // skw_dbg("dev: %s, mac: %pM\n", netdev_name(dev), mac); if (!mac) return 0; ctx = skw_peer_ctx(iface, mac); if (!ctx) return -ENOENT; memset(&get_sta_resp, 0, sizeof(get_sta_resp)); ts = local_clock(); do_div(ts, 1000000); params.timestamp = ts; skw_ether_copy(params.mac, mac); ret = skw_send_msg(wiphy, dev, SKW_CMD_GET_STA, ¶ms, sizeof(params), &get_sta_resp, sizeof(struct skw_get_sta_resp)); if (ret) { skw_warn("failed, ret: %d\n", ret); return ret; } sinfo->tx_failed = get_sta_resp.tx_failed; sinfo->filled |= SKW_COMPAT_TX_FAILED; sinfo->signal = get_sta_resp.signal; sinfo->filled |= SKW_COMPAT_SIGNAL; if (is_skw_sta_mode(iface)) { sinfo->tx_packets = dev->stats.tx_packets; sinfo->filled |= SKW_COMPAT_TX_PACKETS; sinfo->tx_bytes = dev->stats.tx_bytes; sinfo->filled |= SKW_COMPAT_TX_BYTES; sinfo->rx_packets = dev->stats.rx_packets; sinfo->filled |= SKW_COMPAT_RX_PACKETS; sinfo->rx_bytes = dev->stats.rx_bytes; sinfo->filled |= SKW_COMPAT_RX_BYTES; } skw_set_rate_info(&get_sta_resp.tx_rate, &sinfo->txrate); sinfo->filled |= SKW_COMPAT_TX_BITRATE; skw_peer_ctx_lock(ctx); if (ctx->peer) { ctx->peer->tx.rssi = sinfo->signal; skw_hex_dump("get_sta_rx_rate:", &get_sta_resp.rx_rate, sizeof(get_sta_resp.rx_rate), false); skw_desc_get_rx_rate(&ctx->peer->rx.rate, get_sta_resp.rx_rate.bw, get_sta_resp.rx_rate.ppdu_mode, skw_desc_gi_to_skw_gi(get_sta_resp.rx_rate.gi_type, get_sta_resp.rx_rate.ppdu_mode), skw_desc_nss_to_nss_num(get_sta_resp.rx_rate.nss), get_sta_resp.rx_rate.dcm, get_sta_resp.rx_rate.data_rate); skw_set_rate_info(&ctx->peer->rx.rate, &sinfo->rxrate); sinfo->filled |= SKW_COMPAT_RX_BITRATE; memcpy(&ctx->peer->tx.rate, &get_sta_resp.tx_rate, sizeof(struct skw_rate)); ctx->peer->tx.tx_psr = get_sta_resp.tx_psr; ctx->peer->tx.tx_failed = get_sta_resp.tx_failed; memcpy(ctx->peer->rx.filter_cnt, get_sta_resp.filter_cnt, sizeof(get_sta_resp.filter_cnt)); memcpy(ctx->peer->rx.filter_drop_offload_cnt, get_sta_resp.filter_drop_offload_cnt, sizeof(get_sta_resp.filter_drop_offload_cnt)); ctx->peer->tx.percent = get_sta_resp.tx_percent; ctx->peer->rx.percent = get_sta_resp.rx_percent; if (is_skw_ap_mode(iface)) { sinfo->tx_packets = ctx->peer->tx.pkts; sinfo->tx_bytes = ctx->peer->tx.bytes; sinfo->rx_packets = ctx->peer->rx.pkts; sinfo->rx_bytes = ctx->peer->rx.bytes; sinfo->filled |= SKW_COMPAT_TX_PACKETS | SKW_COMPAT_TX_BYTES | SKW_COMPAT_RX_PACKETS | SKW_COMPAT_RX_BYTES; } } skw_peer_ctx_unlock(ctx); // skw_dbg("tx packets:%u tx_bytes:%llu rx_packets:%u rx_bytes:%llu\n", // sinfo->tx_packets, sinfo->tx_bytes, // sinfo->rx_packets, sinfo->rx_bytes); return ret; } static int skw_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) const u8 *mac, #else u8 *mac, #endif struct station_info *sinfo) { return skw_get_station(wiphy, dev, (const u8 *)mac, sinfo); } static void skw_scan_timeout(void *data) { struct skw_iface *iface = data; if (unlikely(!iface)) { skw_warn("iface is NULL\n"); return; } skw_queue_work(priv_to_wiphy(iface->skw), iface, SKW_WORK_SCAN_TIMEOUT, NULL, 0); } static bool skw_cqm_bg_scan(struct skw_iface *iface, struct cfg80211_scan_request *req, u16 *target_chn) { bool ret; if (iface->wdev.iftype != NL80211_IFTYPE_STATION) return false; spin_lock_bh(&iface->sta.roam_data.lock); if (iface->sta.roam_data.flags & SKW_IFACE_STA_ROAM_FLAG_CQM_LOW && iface->sta.core.sm.state == SKW_STATE_COMPLETED && req->n_channels > 10 && req->n_ssids == 1 && req->ssids != NULL && req->ssids->ssid_len != 0 && req->ssids->ssid_len == iface->sta.core.bss.ssid_len && memcmp(req->ssids->ssid, iface->sta.core.bss.ssid, req->ssids->ssid_len) == 0) { skw_dbg("only %d", iface->sta.roam_data.target_chn); *target_chn = iface->sta.roam_data.target_chn; iface->sta.roam_data.flags &= ~SKW_IFACE_STA_ROAM_FLAG_CQM_LOW; skw_del_timer_work(iface->skw, skw_cqm_scan_timeout); ret = true; } else { *target_chn = 0; ret = false; } spin_unlock_bh(&iface->sta.roam_data.lock); return ret; } static bool is_skw_6ghz_non_psc_chan(struct ieee80211_channel *channel) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) if (channel->band != NL80211_BAND_6GHZ) return false; if (channel->hw_value % 16 != 5) return true; #endif return false; } static int skw_scan(struct wiphy *wiphy, struct cfg80211_scan_request *req) { int i, ret; struct skw_scan_chan_info *chan; int size, nssids_size, offset; u16 roam_chn = 0; u16 scan_chn_num = 0; char *buff = NULL; struct skw_scan_param *param = NULL; struct skw_core *skw = wiphy_priv(wiphy); struct skw_iface *iface = SKW_WDEV_TO_IFACE(req->wdev); skw_dbg("%s: chip: %d, nr_chan: %d, n_ssids: %d, ie_len: %zd\n", skw_iftype_name(req->wdev->iftype), skw->idx, req->n_channels, req->n_ssids, req->ie_len); size = sizeof(struct skw_scan_param) + req->n_channels * sizeof(*chan) + req->n_ssids * sizeof(struct cfg80211_ssid) + req->ie_len; buff = SKW_ZALLOC(size, GFP_KERNEL); if (!buff) { skw_err("malloc failed, size: %d\n", size); return -ENOMEM; } offset = 0; param = (struct skw_scan_param *)buff; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR && iface->wdev.iftype == NL80211_IFTYPE_STATION) { param->flags |= SKW_SCAN_FLAG_RND_MAC; get_random_mask_addr(param->rand_mac, req->mac_addr, req->mac_addr_mask); } #endif if (iface->wdev.iftype == NL80211_IFTYPE_AP) { param->flags |= SKW_SCAN_FLAG_ACS; skw_purge_survey_data(iface); } offset += sizeof(struct skw_scan_param); param->chan_offset = offset; chan = (struct skw_scan_chan_info *)(buff + offset); skw_cqm_bg_scan(iface, req, &roam_chn); for (i = 0; i < req->n_channels; i++) { if (unlikely(iface->extend.scan_band_filter)) { if (!(iface->extend.scan_band_filter & BIT(req->channels[i]->band))) continue; } if (unlikely(roam_chn && roam_chn != req->channels[i]->hw_value)) continue; if (is_skw_6ghz_non_psc_chan(req->channels[i])) continue; chan->band = to_skw_band(req->channels[i]->band); chan->chan_num = req->channels[i]->hw_value; if ((req->channels[i]->flags & SKW_PASSIVE_SCAN) || (!req->n_ssids && is_skw_sta_mode(iface))) chan->scan_flags |= SKW_SCAN_FLAG_PASSIVE; scan_chn_num++; chan++; } param->nr_chan = scan_chn_num; offset += scan_chn_num * sizeof(*chan); param->n_ssid = req->n_ssids; if (req->n_ssids) { nssids_size = req->n_ssids * sizeof(struct cfg80211_ssid); memcpy(buff + offset, req->ssids, nssids_size); param->ssid_offset = offset; offset += nssids_size; } if (req->ie_len) { memcpy(buff + offset, req->ie, req->ie_len); param->ie_offset = offset; param->ie_len = req->ie_len; } skw->scan_req = req; skw->nr_scan_results = 0; skw_add_timer_work(skw, "scan_timeout", skw_scan_timeout, iface, SKW_SCAN_TIMEOUT, req, GFP_KERNEL); ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_START_SCAN, buff, size, NULL, 0); if (ret) { skw->scan_req = NULL; skw_del_timer_work(skw, req); skw_dbg("failed, ret: %d\n", ret); } SKW_KFREE(buff); return ret; } void skw_scan_done(struct skw_core *skw, struct skw_iface *iface, bool aborted) { struct cfg80211_scan_request *scan_req; int ret_val; mutex_lock(&skw->lock); if (!skw->scan_req) goto ret; if (&iface->wdev != skw->scan_req->wdev) goto ret; skw_dbg("inst: %d, aborted: %d, scan result: %d\n", iface->id, aborted, skw->nr_scan_results); scan_req = skw->scan_req; skw->scan_req = NULL; skw_del_timer_work(skw, scan_req); if (aborted) { ret_val = skw_msg_xmit(priv_to_wiphy(skw), iface->id, SKW_CMD_STOP_SCAN, NULL, 0, NULL, 0); if (ret_val) skw_warn("failed, return: %d\n", ret_val); } skw_compat_scan_done(scan_req, aborted); ret: mutex_unlock(&skw->lock); } static void skw_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) { struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); struct skw_core *skw = wiphy_priv(wiphy); int ret; skw_dbg("inst: %d, scaning: %d\n", iface->id, !!skw->scan_req); if (!skw->scan_req) return; ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_STOP_SCAN, NULL, 0, NULL, 0); if (ret) skw_err("failed, ret: %d\n", ret); skw_scan_done(skw, iface, false); } static int skw_mbssid_index(struct skw_core *skw, struct cfg80211_bss *bss) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)) return skw_bss_priv(bss)->bssid_index; #else return bss->bssid_index; #endif } static int skw_mbssid_max_indicator(struct skw_core *skw, struct cfg80211_bss *bss) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)) return skw_bss_priv(bss)->max_bssid_indicator; #else return bss->max_bssid_indicator; #endif } const u8 *skw_bss_get_ext_ie(struct cfg80211_bss *bss, u8 ext_eid) { const struct cfg80211_bss_ies *ies; ies = rcu_dereference(bss->ies); if (!ies) return NULL; return skw_find_ie_match(SKW_WLAN_EID_EXTENSION, ies->data, ies->len, &ext_eid, 1, 2); } static int skw_set_he_mib(struct wiphy *wiphy, struct net_device *ndev, int he_enable) { int ret; u16 *plen; struct skw_tlv_conf conf; skw_dbg("he_enable: %d\n", he_enable); ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) { skw_err("alloc failed\n"); return ret; } plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_err("reserve failed\n"); skw_tlv_free(&conf); return -ENOMEM; } if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_HE, &he_enable, 4)) { skw_err("set HE mode [%d] failed\n", he_enable); skw_tlv_free(&conf); return -EINVAL; } *plen = conf.total_len; ret = skw_send_msg(wiphy, ndev, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_warn("failed, ret: %d\n", ret); skw_tlv_free(&conf); return ret; } static int skw_set_vht_mib(struct wiphy *wiphy, struct net_device *ndev, int vht_enable) { int ret; u16 *plen; struct skw_tlv_conf conf; skw_dbg("vht_enable: %d\n", vht_enable); ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) { skw_err("alloc failed\n"); return ret; } plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_err("reserve failed\n"); skw_tlv_free(&conf); return -ENOMEM; } if (skw_tlv_add(&conf, SKW_MIB_DOT11_MODE_VHT, &vht_enable, 4)) { skw_err("set vht mode [%d] failed\n", vht_enable); skw_tlv_free(&conf); return -EINVAL; } *plen = conf.total_len; ret = skw_send_msg(wiphy, ndev, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_warn("failed, ret: %d\n", ret); skw_tlv_free(&conf); return ret; } static void skw_parse_center_chn(struct cfg80211_bss *bss, int *he_enable, struct skw_center_chn *cc) { unsigned int diff; const u8 *ht_ie, *vht_ie; u8 vht_seg0_idx, vht_seg1_idx; struct ieee80211_ht_operation *ht_oper; struct ieee80211_vht_operation *vht_oper; const u8 *he_ie; struct skw_he_cap_elem *he_cap; cc->bw = SKW_CHAN_WIDTH_20; cc->center_chn1 = bss->channel->hw_value; cc->center_chn2 = 0; *he_enable = 1; if (WARN_ON(!bss)) return; rcu_read_lock(); ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_OPERATION); if (ht_ie && ht_ie[1]) { ht_oper = (struct ieee80211_ht_operation *)(ht_ie + 2); cc->center_chn2 = 0; switch (ht_oper->ht_param & 0x3) { case IEEE80211_HT_PARAM_CHA_SEC_NONE: cc->bw = SKW_CHAN_WIDTH_20; cc->center_chn1 = ht_oper->primary_chan; break; case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: cc->bw = SKW_CHAN_WIDTH_40; cc->center_chn1 = ht_oper->primary_chan + 2; break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: cc->bw = SKW_CHAN_WIDTH_40; cc->center_chn1 = ht_oper->primary_chan - 2; break; default: break; } } vht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); if (vht_ie && vht_ie[1]) { vht_oper = (struct ieee80211_vht_operation *)(vht_ie + 2); cc->center_chn2 = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) vht_seg0_idx = vht_oper->center_freq_seg0_idx; vht_seg1_idx = vht_oper->center_freq_seg1_idx; #else vht_seg0_idx = vht_oper->center_freq_seg1_idx; vht_seg1_idx = vht_oper->center_freq_seg2_idx; #endif switch (vht_oper->chan_width) { case IEEE80211_VHT_CHANWIDTH_80MHZ: cc->bw = SKW_CHAN_WIDTH_80; cc->center_chn1 = vht_seg0_idx; if (vht_seg1_idx) { diff = abs(vht_seg1_idx - vht_seg0_idx); if (diff == 8) { cc->bw = SKW_CHAN_WIDTH_160; cc->center_chn1 = vht_seg1_idx; } else if (diff > 8) { cc->bw = SKW_CHAN_WIDTH_80P80; cc->center_chn2 = vht_seg1_idx; } } break; case IEEE80211_VHT_CHANWIDTH_160MHZ: cc->bw = SKW_CHAN_WIDTH_160; cc->center_chn1 = vht_seg0_idx; break; case IEEE80211_VHT_CHANWIDTH_80P80MHZ: cc->bw = SKW_CHAN_WIDTH_80P80; cc->center_chn1 = vht_seg0_idx; cc->center_chn2 = vht_seg1_idx; break; default: break; } } he_ie = skw_bss_get_ext_ie(bss, SKW_WLAN_EID_EXT_HE_CAPABILITY); if (he_ie && he_ie[1]) { skw_hex_dump("he capa", he_ie, he_ie[1] + 2, false); /* 802.11ax D3.0 */ he_cap = (struct skw_he_cap_elem *)(he_ie + 3); // ID: 1 + len: 1 + Num: 1 skw_dbg("band: %d, ppe: 0x%x, phy_cap_info[0]: 0x%x\n", bss->channel->band, he_cap->ppe, he_cap->phy_cap_info[0]); if ((he_cap->phy_cap_info[6] & 0x80) == 0x80 && (he_cap->ppe & 0x78) == 0x60) { // check BIT[3:6] switch (bss->channel->band) { case NL80211_BAND_2GHZ: *he_enable = 0; break; case NL80211_BAND_5GHZ: if (!(he_cap->phy_cap_info[0] & SKW_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G)) *he_enable = 0; break; default: break; } } } cc->band = to_skw_band(bss->channel->band); skw_dbg("cc->bw:%d cc->band:%d\n", cc->bw, cc->band); rcu_read_unlock(); } static int skw_cmd_join(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_bss *bss, u32 bw, u8 band, u16 center_chn1, u16 center_chn2, bool roaming, struct skw_join_resp *resp) { struct skw_core *skw = wiphy_priv(wiphy); struct skw_iface *iface = netdev_priv(ndev); struct skw_join_param *params; int ret = 0, size = 0; skw_dbg("bssid: %pM(idx: %d, ind: %d), chn: %d(%d, %d), bw: %d band: %d\n", bss->bssid, skw_mbssid_index(skw, bss), skw_mbssid_max_indicator(skw, bss), bss->channel->hw_value, center_chn1, center_chn2, bw, band); size = sizeof(struct skw_join_param) + bss->ies->len; params = SKW_ZALLOC(size, GFP_KERNEL); if (!params) return -ENOMEM; params->bandwidth = bw; params->band = band; params->center_chn1 = center_chn1; params->center_chn2 = center_chn2; params->chan_num = bss->channel->hw_value; params->reserved = 0; params->roaming = !!roaming; params->capability = bss->capability; params->beacon_interval = bss->beacon_interval; params->bssid_index = skw_mbssid_index(skw, bss); params->max_bssid_indicator = skw_mbssid_max_indicator(skw, bss); memcpy(params->bssid, bss->bssid, ETH_ALEN); if (bss->ies->len) { memcpy(params->bss_ie, bss->ies->data, bss->ies->len); params->bss_ie_offset = sizeof(struct skw_join_param); params->bss_ie_len = bss->ies->len; } skw_edma_mask_irq(skw, iface->lmac_id); ret = skw_send_msg(wiphy, ndev, SKW_CMD_JOIN, params, size, resp, sizeof(*resp)); if (ret) skw_err("failed, ret: %d\n", ret); SKW_KFREE(params); return ret; } int skw_cmd_unjoin(struct wiphy *wiphy, struct net_device *ndev, const u8 *addr, u16 reason, bool tx_frame) { int ret; struct skw_disconnect_param params; skw_dbg("%s, bssid: %pM, reason: %d\n", netdev_name(ndev), addr, reason); memset(¶ms, 0x0, sizeof(params)); params.type = SKW_DISCONNECT_ONLY; params.reason_code = reason; params.local_state_change = !tx_frame; if (tx_frame) params.type = SKW_DISCONNECT_SEND_DEAUTH; ret = skw_send_msg(wiphy, ndev, SKW_CMD_DISCONNECT, ¶ms, sizeof(params), NULL, 0); if (ret) skw_err("failed, ret: %d\n", ret); return ret; } int skw_cmd_monitor(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, u8 mode) { int ret = 0; struct skw_set_monitor_param param = {0}; param.mode = mode; switch (param.mode) { case SKW_MONITOR_CLOSE: break; case SKW_MONITOR_COMMON: case SKW_MONITOR_MAC_CAP: case SKW_MONITOR_PHY_CAP: if (chandef == NULL || chandef->chan == NULL) return -EINVAL; param.chan_num = chandef->chan->hw_value; param.center_chn1 = skw_freq_to_chn(chandef->center_freq1); param.center_chn2 = skw_freq_to_chn(chandef->center_freq2); param.bandwidth = to_skw_bw(chandef->width); break; default: return -EINVAL; } ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MONITOR_PARAM, ¶m, sizeof(struct skw_set_monitor_param), NULL, 0); return ret; } static int skw_cmd_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { int ret = 0; u16 auth_alg; int size, offset; struct skw_auth_param *params = NULL; struct skw_iface *iface = netdev_priv(dev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) const u8 *auth_data = req->auth_data; size_t auth_data_len = req->auth_data_len; #else const u8 *auth_data = req->sae_data; size_t auth_data_len = req->sae_data_len; #endif switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: auth_alg = WLAN_AUTH_OPEN; break; case NL80211_AUTHTYPE_SHARED_KEY: auth_alg = WLAN_AUTH_SHARED_KEY; break; case NL80211_AUTHTYPE_FT: auth_alg = WLAN_AUTH_FT; break; case NL80211_AUTHTYPE_NETWORK_EAP: auth_alg = WLAN_AUTH_LEAP; break; case NL80211_AUTHTYPE_SAE: auth_alg = WLAN_AUTH_SAE; break; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) case NL80211_AUTHTYPE_FILS_SK: auth_alg = WLAN_AUTH_FILS_SK; break; case NL80211_AUTHTYPE_FILS_SK_PFS: auth_alg = WLAN_AUTH_FILS_SK_PFS; break; case NL80211_AUTHTYPE_FILS_PK: auth_alg = WLAN_AUTH_FILS_PK; break; #endif case NL80211_AUTHTYPE_AUTOMATIC: /* * Fixme: try open wep first, then set share key after using * open wep failed. */ auth_alg = WLAN_AUTH_OPEN; break; default: return -EOPNOTSUPP; } size = sizeof(struct skw_auth_param) + req->ie_len + auth_data_len; params = SKW_ZALLOC(size, GFP_KERNEL); if (!params) { skw_err("malloc failed, size: %d\n", size); return -ENOMEM; } offset = sizeof(struct skw_auth_param); params->auth_algorithm = auth_alg; if (auth_data_len) { params->auth_data_offset = offset; params->auth_data_len = auth_data_len; memcpy((u8 *)params + offset, auth_data, auth_data_len); offset += auth_data_len; } if (req->ie && req->ie_len) { params->auth_ie_offset = offset; params->auth_ie_len = req->ie_len; memcpy((u8 *)params + offset, req->ie, req->ie_len); offset += req->ie_len; } memcpy(iface->sta.core.pending.auth_cmd, params, size); iface->sta.core.pending.auth_cmd_len = size; ret = skw_msg_xmit_timeout(wiphy, SKW_NDEV_ID(dev), SKW_CMD_AUTH, params, size, NULL, 0, "SKW_CMD_AUTH", msecs_to_jiffies(SKW_CMD_TIMEOUT), 0); SKW_KFREE(params); return ret; } static inline void skw_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask) { int i; u8 *p1, *p2; if (!ht_capa_mask) { memset(ht_capa, 0, sizeof(*ht_capa)); return; } p1 = (u8 *)(ht_capa); p2 = (u8 *)(ht_capa_mask); for (i = 0; i < sizeof(*ht_capa); i++) p1[i] &= p2[i]; } /* Do a logical ht_capa &= ht_capa_mask. */ static inline void skw_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, const struct ieee80211_vht_cap *vht_capa_mask) { int i; u8 *p1, *p2; if (!vht_capa_mask) { memset(vht_capa, 0, sizeof(*vht_capa)); return; } p1 = (u8 *)(vht_capa); p2 = (u8 *)(vht_capa_mask); for (i = 0; i < sizeof(*vht_capa); i++) p1[i] &= p2[i]; } static int skw_cmd_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { int ret = 0; int size, offset; char *buff = NULL; struct skw_assoc_req_param *param = NULL; struct skw_iface *iface = netdev_priv(dev); size = sizeof(struct skw_assoc_req_param) + req->ie_len; buff = SKW_ZALLOC(size, GFP_KERNEL); if (!buff) { skw_err("malloc failed, size: %d\n", size); return -ENOMEM; } offset = 0; param = (struct skw_assoc_req_param *)buff; memcpy(¶m->ht_capa, &req->ht_capa, sizeof(req->ht_capa)); skw_oper_and_ht_capa(¶m->ht_capa, &req->ht_capa_mask); memcpy(¶m->vht_capa, &req->vht_capa, sizeof(req->vht_capa)); skw_oper_and_vht_capa(¶m->vht_capa, &req->vht_capa_mask); memcpy(param->bssid, req->bss->bssid, ETH_ALEN); if (req->prev_bssid) memcpy(param->pre_bssid, req->prev_bssid, ETH_ALEN); param->req_ie_len = req->ie_len; offset += sizeof(struct skw_assoc_req_param); param->req_ie_offset = offset; if (req->ie_len) memcpy(param->req_ie, req->ie, req->ie_len); memcpy(iface->sta.core.pending.assoc_cmd, buff, size); iface->sta.core.pending.assoc_cmd_len = size; ret = skw_msg_xmit_timeout(wiphy, SKW_NDEV_ID(dev), SKW_CMD_ASSOC, buff, size, NULL, 0, "SKW_CMD_ASSOC", msecs_to_jiffies(SKW_CMD_TIMEOUT), 0); SKW_KFREE(buff); return ret; } static void skw_fix_compatibility_issues(struct wiphy *wiphy, struct skw_iface *iface, struct cfg80211_bss *bss, int he_enable, struct skw_center_chn *cc) { struct net_device *ndev = iface->ndev; if (ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return; skw_set_he_mib(wiphy, ndev, he_enable); #if 0 // disabled for lite rcu_read_lock(); he_oper_ie = skw_bss_get_ext_ie(bss, SKW_WLAN_EID_EXT_HE_OPERATION); if (he_oper_ie) { he_oper = (struct skw_he_oper_elem *)(he_oper_ie + 3); if (he_oper->bss_color == 0 && (int)bss->channel->band == (int)NL80211_BAND_5GHZ && !(skw->fw.fw_bw_capa & (SKW_BW_5GHZ_80M | SKW_BW_5GHZ_160M | SKW_BW_5GHZ_8080M))) { if (he_oper->bss_color_disabled == 0) { skw_set_vht_mib(wiphy, ndev, 0); skw_info("Disable VHT"); } else { if (cc->bw >= SKW_CHAN_WIDTH_80 && skw_bss_check_vendor_name(bss, oui)) { skw_set_he_mib(wiphy, ndev, 0); skw_info("Disable HE"); } } } } rcu_read_unlock(); #endif } static int skw_join(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_bss *bss, bool roaming) { int ret = 0, he_enable; struct skw_peer *peer; struct skw_peer_ctx *ctx; struct skw_center_chn cc = {}; struct skw_join_resp resp = {}; struct skw_iface *iface = netdev_priv(ndev); struct skw_sta_core *core = &iface->sta.core; skw_wdev_assert_lock(iface); peer = skw_peer_alloc(); if (!peer) { skw_err("alloc peer failed\n"); return -ENOMEM; } skw_parse_center_chn(bss, &he_enable, &cc); skw_fix_compatibility_issues(wiphy, iface, bss, he_enable, &cc); SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); ret = skw_cmd_join(wiphy, ndev, bss, cc.bw, cc.band, cc.center_chn1, cc.center_chn2, roaming, &resp); if (ret < 0) { skw_err("command join failed, ret: %d\n", ret); SKW_KFREE(peer); return ret; } skw_peer_init(peer, bss->bssid, resp.peer_idx); ctx = skw_get_ctx(iface->skw, resp.lmac_id, resp.peer_idx); ret = skw_peer_ctx_bind(iface, ctx, peer); if (ret) { skw_cmd_unjoin(wiphy, ndev, bss->bssid, SKW_LEAVE, false); SKW_KFREE(peer); return -EFAULT; } skw_join_resp_handler(wiphy_priv(wiphy), iface, &resp); skw_ether_copy(core->bss.bssid, bss->bssid); core->bss.channel = bss->channel; core->bss.ctx_idx = resp.peer_idx; core->bss.width = cc.bw; skw_dpd_set_coeff_params(wiphy, ndev, bss->channel->hw_value, cc.center_chn1, cc.center_chn2, cc.bw); if (!iface->sta.sme_external) { if (!is_valid_ether_addr(iface->sta.conn->prev_bssid)) core->bss.auth_type = iface->sta.conn->auth_type; } return 0; } static int skw_unjoin(struct wiphy *wiphy, struct net_device *ndev, const u8 *bssid, u16 reason, bool tx_frame) { int ret = 0; struct skw_peer_ctx *ctx; struct skw_iface *iface = netdev_priv(ndev); skw_dbg("bssid: %pM, reason: %d\n", bssid, reason); if (ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION) { skw_set_he_mib(wiphy, ndev, 1); skw_set_vht_mib(wiphy, ndev, 1); } ctx = skw_peer_ctx(iface, bssid); if (!ctx) { skw_warn("bssid: %pM not exist\n", bssid); return 0; } skw_peer_ctx_transmit(ctx, false); iface->sta.is_wep = false; SKW_SET(iface->flags, SKW_IFACE_FLAG_DEAUTH); ret = skw_cmd_unjoin(wiphy, ndev, bssid, reason, tx_frame); if (!ret) { memset(&iface->sta.core.bss, 0x0, sizeof(iface->sta.core.bss)); iface->sta.core.bss.ctx_idx = SKW_INVALID_ID; skw_lmac_unbind_iface(wiphy_priv(wiphy), iface->lmac_id, iface->id); skw_peer_ctx_bind(iface, ctx, NULL); } else { skw_warn("command unjoin failed, ret: %d\n", ret); SKW_CLEAR(iface->flags, SKW_IFACE_FLAG_DEAUTH); } return ret; } int skw_sta_leave(struct wiphy *wiphy, struct net_device *dev, const u8 *bssid, u16 reason, bool tx_frame) { int i; struct skw_iface *iface = netdev_priv(dev); skw_dbg("bssid: %pM, reason: %d\n", bssid, reason); skw_wdev_assert_lock(iface); netif_carrier_off(dev); if (iface->skw->hw.bus == SKW_BUS_PCIE && iface->sta.core.sm.state >= SKW_STATE_ASSOCED) skw_edma_dec_refill((void *)iface->skw, iface->lmac_id); memset(&iface->wmm, 0x0, sizeof(iface->wmm)); del_timer_sync(&iface->sta.core.timer); skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); iface->sta.core.sm.flags = 0; skw_unjoin(wiphy, dev, bssid, reason, tx_frame); skw_purge_key_conf(&iface->key_conf); memset(iface->sta.core.bss.ssid, 0x0, IEEE80211_MAX_SSID_LEN); iface->sta.core.bss.ssid_len = 0; for (i = 0; i < SKW_MAX_DEFRAG_ENTRY; i++) { skb_queue_purge(&iface->frag[i].skb_list); iface->frag[i].tid = SKW_INVALID_ID; } return 0; } void skw_tx_mlme_mgmt(struct net_device *dev, u16 stype, const u8 *bssid, const u8 *da, u16 reason) { struct ieee80211_mgmt mgmt; struct skw_iface *iface = netdev_priv(dev); mgmt.duration = 0; mgmt.seq_ctrl = 0; memcpy(mgmt.da, da, ETH_ALEN); memcpy(mgmt.sa, iface->addr, ETH_ALEN); memcpy(mgmt.bssid, bssid, ETH_ALEN); mgmt.u.deauth.reason_code = cpu_to_le16(reason); mgmt.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); skw_cfg80211_tx_mlme_mgmt(dev, (void *)&mgmt, SKW_DEAUTH_FRAME_LEN); } static int skw_auth(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_auth_request *req) { int ret; struct key_params key; bool roaming = false; struct skw_iface *iface = netdev_priv(ndev); struct skw_bss_cfg *bss = &iface->sta.core.bss; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) const u8 *auth_data = req->auth_data; #else const u8 *auth_data = req->sae_data; #endif skw_info("%s, bssid: %pM, auth type: %d, state: %s\n", netdev_name(ndev), req->bss->bssid, req->auth_type, skw_state_name(iface->sta.core.sm.state)); skw_wdev_assert_lock(iface); #ifdef CONFIG_SWT6621S_USB3_WORKAROUND ret = skw_switch_usb3_to_usb2_using_2G(iface, req->bss->channel->band); if (ret) return ret; #endif skw_abort_scan(wiphy, ndev->ieee80211_ptr); // skw_scan_done(iface->skw, iface, true); // skw_sched_scan_stop(wiphy, ndev, iface->skw->sched_scan_req->reqid); switch (iface->sta.core.sm.state) { case SKW_STATE_AUTHING: case SKW_STATE_ASSOCING: return -EBUSY; case SKW_STATE_ASSOCED: case SKW_STATE_COMPLETED: if (ether_addr_equal(bss->bssid, req->bss->bssid)) return 0; roaming = true; if (iface->sta.sme_external) skw_tx_mlme_mgmt(iface->ndev, IEEE80211_STYPE_DEAUTH, iface->sta.core.bss.bssid, iface->sta.core.bss.bssid, SKW_LEAVE); skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); ret = skw_unjoin(wiphy, ndev, bss->bssid, SKW_LEAVE, false); if (ret) return ret; /* fall through */ skw_fallthrough; case SKW_STATE_NONE: if (is_valid_ether_addr(bss->bssid)) { skw_warn("unexpected bssid: %pM\n", bss->bssid); ret = skw_unjoin(wiphy, ndev, bss->bssid, 3, false); if (ret) return ret; } if (!skw_channel_allowed(wiphy, req->bss->channel->hw_value)) return -EBUSY; ret = skw_join(wiphy, ndev, req->bss, roaming); if (ret < 0) return ret; break; default: break; } if (req->key && req->key_len) { key.seq = NULL; key.seq_len = 0; key.key = (u8 *)req->key; key.key_len = req->key_len; key.cipher = SKW_CIPHER_SUITE_WEP40; if (req->key_len != 5) key.cipher = SKW_CIPHER_SUITE_WEP104; ret = skw_add_key(wiphy, ndev, 0, req->key_idx, false, NULL, &key); if (ret < 0) { skw_err("add share key failed, ret: %d\n", ret); goto unjoin; } iface->sta.is_wep = true; skw_set_default_key(wiphy, ndev, 0, req->key_idx, true, true); } iface->sta.core.auth_start = jiffies; iface->sta.core.pending.retry = 0; iface->sta.core.pending.redo = 0; iface->sta.core.pending.step_start = jiffies; iface->sta.core.pending.ctx_start = jiffies; iface->sta.core.pending.auth_type = req->auth_type; iface->sta.core.sm.rty_state = SKW_RETRY_NONE; iface->sta.core.pending.ctx_to = SKW_AUTH_TIMEOUT; skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHING); ret = skw_cmd_auth(wiphy, ndev, req); if (ret) { skw_dbg("command auth failed, ret: %d\n", ret); goto unjoin; } skw_set_sta_timer(&iface->sta.core, SKW_STEP_TIMEOUT); /* SAE confirm */ if (auth_data && le16_to_cpu(*((u16 *)auth_data) == 2) && iface->sta.core.sm.flags & SKW_SM_FLAG_SAE_RX_CONFIRM) skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHED); iface->sta.report_deauth = true; return 0; unjoin: skw_unjoin(wiphy, ndev, req->bss->bssid, SKW_LEAVE, false); skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); return ret; } static int skw_cfg80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { struct skw_iface *iface = netdev_priv(dev); iface->sta.report_deauth = false; return skw_auth(wiphy, dev, req); } static int skw_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { int ret; const u8 *ssid_ie; struct skw_iface *iface = netdev_priv(dev); struct skw_sta_core *core = &iface->sta.core; skw_dbg("%s, bssid: %pM\n", netdev_name(dev), req->bss->bssid); skw_wdev_assert_lock(iface); switch (core->sm.state) { case SKW_STATE_AUTHING: case SKW_STATE_ASSOCING: return -EBUSY; case SKW_STATE_ASSOCED: case SKW_STATE_COMPLETED: if (ether_addr_equal(core->bss.bssid, req->bss->bssid)) return 0; skw_set_state(&core->sm, SKW_STATE_NONE); ret = skw_unjoin(wiphy, dev, core->bss.bssid, SKW_LEAVE, false); if (ret) return ret; ret = skw_join(wiphy, dev, req->bss, true); if (ret) return ret; skw_set_state(&core->sm, SKW_STATE_AUTHED); break; /* continue */ case SKW_STATE_AUTHED: break; default: return -EINVAL; } rcu_read_lock(); ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); if (ssid_ie) { memcpy(core->bss.ssid, ssid_ie + 2, ssid_ie[1]); core->bss.ssid_len = ssid_ie[1]; } rcu_read_unlock(); core->cbss = req->bss; core->pending.retry = 0; core->pending.ctx_start = jiffies; iface->sta.core.pending.ctx_to = SKW_ASSOC_TIMEOUT; core->assoc_req_ie_len = 0; memset(core->assoc_req_ie, 0x0, SKW_2K_SIZE); skw_set_state(&core->sm, SKW_STATE_ASSOCING); ret = skw_cmd_assoc(wiphy, dev, req); if (!ret) { skw_set_sta_timer(core, SKW_STEP_TIMEOUT); } else { skw_err("command assoc failed, ret: %d\n", ret); core->cbss = NULL; del_timer_sync(&core->timer); skw_unjoin(wiphy, dev, req->bss->bssid, SKW_LEAVE, false); skw_set_state(&core->sm, SKW_STATE_NONE); memset(core->bss.ssid, 0x0, IEEE80211_MAX_SSID_LEN); core->bss.ssid_len = 0; } return ret; } static int skw_cfg80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { return skw_assoc(wiphy, dev, req); } static int skw_cfg80211_deauth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_deauth_request *req) { int ret; struct skw_iface *iface = netdev_priv(dev); bool tx_frame = !req->local_state_change && iface->sta.report_deauth; skw_info("%s: bssid: %pM, reason: %d, tx frame: %d\n", netdev_name(dev), req->bssid, req->reason_code, tx_frame); ret = skw_sta_leave(wiphy, dev, req->bssid, req->reason_code, tx_frame); if (!ret && iface->sta.report_deauth) { skw_tx_mlme_mgmt(dev, IEEE80211_STYPE_DEAUTH, req->bssid, req->bssid, req->reason_code); } else { skw_err("failed, ret: %d\n", ret); } return ret; } static int skw_disassoc(struct wiphy *wiphy, struct net_device *dev, const u8 *bssid, u16 reason, bool tx_frame) { int ret = 0; skw_info("%s, bssid: %pM, reason: %d, tx frame: %d\n", netdev_name(dev), bssid, reason, tx_frame); ret = skw_sta_leave(wiphy, dev, bssid, reason, tx_frame); if (ret) { skw_err("failed, ret: %d\n", ret); return ret; } skw_tx_mlme_mgmt(dev, IEEE80211_STYPE_DISASSOC, bssid, bssid, reason); return 0; } void skw_connected(struct net_device *dev, struct skw_connect_param *conn, const u8 *req_ie, int req_ie_len, const u8 *resp_ie, int resp_ie_len, u16 status, gfp_t gfp) { if (conn->flags & SKW_CONN_FLAG_ASSOCED) { skw_compat_cfg80211_roamed(dev, conn->bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); } else { cfg80211_connect_result(dev, conn->bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, gfp); } SKW_SET(conn->flags, SKW_CONN_FLAG_ASSOCED); } void skw_disconnected(struct net_device *dev, u16 reason, const u8 *resp_ie, int resp_ie_len, bool local_gen, gfp_t gfp) { struct skw_iface *iface = netdev_priv(dev); struct skw_connect_param *conn = iface->sta.conn; mutex_lock(&conn->lock); if (conn->flags & SKW_CONN_FLAG_ASSOCED) { skw_compat_disconnected(dev, reason, NULL, 0, local_gen, gfp); } else { cfg80211_connect_result(dev, conn->bssid, NULL, 0, resp_ie, resp_ie_len, reason, gfp); } SKW_CLEAR(iface->sta.conn->flags, SKW_CONN_FLAG_ASSOCED); mutex_unlock(&conn->lock); } int skw_connect_sae_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_bss *bss) { int ret = 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) bool roaming = false; struct skw_iface *iface = netdev_priv(dev); struct skw_connect_param *conn = iface->sta.conn; struct cfg80211_external_auth_params params; if (!bss) { cfg80211_connect_result(dev, conn->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); return -EINVAL; } // TODO: // unjoin prev bssid for roaming connection roaming = is_valid_ether_addr(conn->prev_bssid); ret = skw_join(wiphy, dev, bss, roaming); if (ret < 0) { skw_err("join %pM failed\n", conn->bssid); return ret; } skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHING); params.action = NL80211_EXTERNAL_AUTH_START; memcpy(params.bssid, conn->bssid, ETH_ALEN); params.ssid.ssid_len = conn->ssid_len; memcpy(params.ssid.ssid, conn->ssid, conn->ssid_len); params.key_mgmt_suite = cpu_to_be32(WLAN_AKM_SUITE_SAE); params.status = WLAN_STATUS_SUCCESS; ret = cfg80211_external_auth_request(dev, ¶ms, GFP_KERNEL); if (ret) { skw_err("failed, ret: %d\n", ret); skw_unjoin(wiphy, dev, conn->bssid, SKW_LEAVE, false); skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); cfg80211_connect_result(dev, conn->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); } #endif return ret; } int skw_connect_auth(struct wiphy *wiphy, struct net_device *dev, struct skw_connect_param *conn, struct cfg80211_bss *bss) { struct cfg80211_auth_request req; if (!bss) { skw_warn("Invalid bss\n"); return -EINVAL; } memset(&req, 0x0, sizeof(req)); req.bss = bss; req.key = conn->key_len ? conn->key : NULL; req.key_len = conn->key_len; req.key_idx = conn->key_idx; req.auth_type = conn->auth_type; return skw_auth(wiphy, dev, &req); } int skw_connect_assoc(struct wiphy *wiphy, struct net_device *ndev, struct skw_connect_param *conn) { int ret = 0; struct cfg80211_assoc_request req = {}; req.bss = cfg80211_get_bss(wiphy, conn->channel, conn->bssid, conn->ssid, conn->ssid_len, SKW_BSS_TYPE_ESS, SKW_PRIVACY_ESS_ANY); if (!req.bss) { skw_info("cfg80211_get_bss null\n"); return -ENOENT; } req.ie = conn->assoc_ie; req.ie_len = conn->assoc_ie_len; req.prev_bssid = conn->prev_bssid; req.use_mfp = conn->flags & SKW_CONN_FLAG_USE_MFP; req.flags = conn->flags; req.ht_capa = conn->ht_capa; req.ht_capa_mask = conn->ht_capa_mask; req.vht_capa = conn->vht_capa; req.vht_capa_mask = conn->vht_capa_mask; ret = skw_assoc(wiphy, ndev, &req); cfg80211_put_bss(wiphy, req.bss); return ret; } int skw_roam_connect(struct skw_iface *iface, const u8 *bssid, u8 chn, enum nl80211_band band) { struct ieee80211_channel *req_channel = NULL; struct wiphy *wiphy = iface->wdev.wiphy; struct skw_connect_param *conn = iface->sta.conn; u32 freq = 0; if (!is_valid_ether_addr(bssid)) return -EINVAL; skw_dbg("roam from %pM to %pM auth_type: %d, chn: %d, band: %d\n", conn->bssid, bssid, conn->auth_type, chn, band); freq = ieee80211_channel_to_frequency(chn, band); req_channel = ieee80211_get_channel(wiphy, freq); if (!req_channel) { skw_err("invalid channel: %d\n", chn); return -EINVAL; } #if 0 iface->sta.backup = SKW_KMEMDUP(&iface->sta.core.bss, sizeof(iface->sta.core.bss), GFP_KERNEL); if (!iface->sta.backup) return -EINVAL; // skw_peer_transmit(); memset(&iface->sta.core.bss, 0x0, sizeof(iface->sta.core.bss)); #endif conn->channel = req_channel; skw_ether_copy(conn->bssid, bssid); skw_ether_copy(conn->prev_bssid, iface->sta.core.bss.bssid); conn->auth_type = iface->sta.conn->auth_type; skw_queue_local_event(priv_to_wiphy(iface->skw), iface, SKW_EVENT_LOCAL_STA_CONNECT, NULL, 0); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) static int skw_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) { struct skw_set_cqm_rssi_param cqm_param; skw_dbg("dev: %s, thold: %d, hyst: %d\n", netdev_name(dev), rssi_thold, rssi_hyst); //TBD: whether to store the config at host driver cqm_param.rssi_thold = rssi_thold; cqm_param.rssi_hyst = (u8)rssi_hyst; return skw_send_msg(wiphy, dev, SKW_CMD_SET_CQM_RSSI, &cqm_param, sizeof(cqm_param), NULL, 0); } static int skw_set_cqm_rssi_range_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_low, s32 rssi_high) { struct skw_set_cqm_rssi_param cqm_param = {0}; s32 val = rssi_high - rssi_low; skw_dbg("dev: %s, rssi_low: %d, rssi_high: %d\n", netdev_name(dev), rssi_low, rssi_high); cqm_param.rssi_thold = rssi_low; if (val < 0) { skw_warn("rssi err\n"); return -EINVAL; } else { cqm_param.rssi_hyst = (u8)val; } return skw_send_msg(wiphy, dev, SKW_CMD_SET_CQM_RSSI, &cqm_param, sizeof(cqm_param), NULL, 0); } #endif static int skw_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_connect_params *req) { struct skw_iface *iface = netdev_priv(ndev); struct skw_connect_param *conn = iface->sta.conn; const u8 *bssid = skw_compat_bssid(req); struct ieee80211_channel *channel = skw_compat_channel(req); skw_dbg("%s, ssid: %s, bssid: %pM, auth: %d, chn: %d key_len: %d\n", netdev_name(ndev), req->ssid, bssid, req->auth_type, channel->hw_value, req->key_len); if (!conn) { skw_dbg("conn is NULL\n"); return -ENOMEM; } if (unlikely(req->ssid_len > IEEE80211_MAX_SSID_LEN)) { skw_err("Invalid SSID: %s, len: %zd\n", req->ssid, req->ssid_len); return -EINVAL; } mutex_lock(&conn->lock); skw_ether_copy(conn->bssid, bssid); eth_zero_addr(conn->prev_bssid); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) if (req->prev_bssid) skw_ether_copy(conn->prev_bssid, req->prev_bssid); #endif if (req->ie && req->ie_len) memcpy(conn->assoc_ie, req->ie, req->ie_len); conn->assoc_ie_len = req->ie_len; if (req->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { conn->auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; SKW_SET(conn->flags, SKW_CONN_FLAG_AUTH_AUTO); } if (req->key && req->key_len) { memcpy(conn->key, req->key, req->key_len); conn->key_len = req->key_len; conn->key_idx = req->key_idx; SKW_SET(conn->flags, SKW_CONN_FLAG_KEY_VALID); } else conn->key_len = 0; conn->ssid_len = req->ssid_len; memcpy(conn->ssid, req->ssid, req->ssid_len); conn->auth_type = req->auth_type; conn->ht_capa = req->ht_capa; conn->vht_capa = req->vht_capa; conn->ht_capa_mask = req->ht_capa_mask; conn->vht_capa_mask = req->vht_capa_mask; mutex_unlock(&conn->lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) if (iface->wdev.iftype == NL80211_IFTYPE_STATION) { skw_set_cqm_rssi_config(wiphy, ndev, SKW_CQM_DEFAUT_RSSI_THOLD, SKW_CQM_DEFAUT_RSSI_HYST); } #endif return skw_queue_local_event(wiphy, iface, SKW_EVENT_LOCAL_STA_CONNECT, NULL, 0); } static int skw_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason) { int ret; struct skw_iface *iface = netdev_priv(dev); struct skw_sta_core *core = &iface->sta.core; skw_info("%s, reason: %d\n", netdev_name(dev), reason); ret = skw_sta_leave(wiphy, dev, core->bss.bssid, reason, true); if (!ret) skw_disconnected(dev, reason, NULL, 0, true, GFP_KERNEL); return ret; } static u64 skw_tx_cookie(void) { static u64 skw_cookie; if (WARN_ON(++skw_cookie == 0)) skw_cookie++; return skw_cookie; } static int skw_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, unsigned int duration, u64 *cookie) { int ret; struct skw_roc_param roc; u64 tx_cookie = skw_tx_cookie(); struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); skw_dbg("iface: %u, chan: %u, band: %u duration: %d, cookie: %llu\n", iface->id, chan->hw_value, chan->band, duration, tx_cookie); roc.enable = 1; roc.channel_num = chan->hw_value; roc.band = to_skw_band(chan->band); roc.duration = duration; roc.cookie = *cookie = tx_cookie; //TBD: define the referenced value if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) roc.channel_type = 2; else if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) roc.channel_type = 1; else if (chan->flags & SKW_IEEE80211_CHAN_NO_20MHZ) roc.channel_type = 0; else roc.channel_type = 3; ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_REMAIN_ON_CHANNEL, &roc, sizeof(roc), NULL, 0); return ret; } static int skw_cancel_roc(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie) { struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); struct skw_roc_param param; skw_dbg("cookie: %lld\n", cookie); #if 0 // fixme: if (cookie != skw->remain_on_channel_cookie) return -ENOENT; #endif memset(¶m, 0x0, sizeof(param)); return skw_msg_xmit(wiphy, iface->id, SKW_CMD_REMAIN_ON_CHANNEL, ¶m, sizeof(param), NULL, 0); } static inline void __skw_set_peer_flags(struct skw_peer_ctx *ctx, u32 flags) { if (ctx) { skw_peer_ctx_lock(ctx); if (ctx->peer) ctx->peer->flags |= flags; skw_peer_ctx_unlock(ctx); } } static void skw_set_peer_flags(struct skw_iface *iface, const u8 *addr, u32 flags) { int idx; struct skw_peer_ctx *ctx; u32 peer_map = atomic_read(&iface->peer_map); if (!addr) return; if (is_unicast_ether_addr(addr)) { ctx = skw_peer_ctx(iface, addr); __skw_set_peer_flags(ctx, flags); return; } while (peer_map) { idx = ffs(peer_map) - 1; SKW_CLEAR(peer_map, BIT(idx)); ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; __skw_set_peer_flags(ctx, flags); } } int skw_mgmt_tx(struct wiphy *wiphy, struct skw_iface *iface, struct ieee80211_channel *chan, u32 wait, u64 *cookie, bool dont_wait_ack, const void *frame, int frame_len, int total_frame_len, const struct ieee80211_mgmt *mgmt, bool switchover) { int ret, total_len; struct skw_mgmt_tx_param *param = NULL; u64 tx_cookie = skw_tx_cookie(); u16 fc = SKW_MGMT_SFC(mgmt->frame_control); if (!chan || !iface) return -EINVAL; if (frame_len > total_frame_len) return -EFBIG; skw_dbg("%s: chan: %d, wait: %d, cookie: 0x%llx, no_ack: %d, len: %d total: %d\n", skw_mgmt_name(fc), chan->hw_value, wait, tx_cookie, dont_wait_ack, frame_len, total_frame_len); skw_hex_dump("mgmt tx", frame, frame_len, false); total_len = sizeof(*param) + frame_len; param = SKW_ZALLOC(total_len, GFP_KERNEL); if (!param) { skw_err("malloc failed, size: %d\n", total_len); return -ENOMEM; } param->wait = wait; param->channel = chan->hw_value; param->band = to_skw_band(chan->band); param->dont_wait_for_ack = dont_wait_ack; param->cookie = *cookie = tx_cookie; memcpy(param->mgmt, frame, frame_len); param->mgmt_frame_len = total_frame_len; ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_TX_MGMT, param, total_len, NULL, 0); if (!ret) { if (switchover) if (is_unicast_ether_addr(mgmt->da) && (fc == IEEE80211_STYPE_DEAUTH || fc == IEEE80211_STYPE_DISASSOC)) { skw_set_peer_flags(iface, mgmt->da, SKW_PEER_FLAG_DEAUTHED); } } else { skw_err("failed, ret: %d\n", ret); } SKW_KFREE(param); return ret; } static inline bool is_skw_rrm_report(const void *buf, int buf_len) { const struct ieee80211_mgmt *mgmt = buf; if (!ieee80211_is_action(mgmt->frame_control)) return false; if (buf_len < IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.measurement)) return false; if (mgmt->u.action.category != SKW_WLAN_CATEGORY_RADIO_MEASUREMENT) return false; if (mgmt->u.action.u.measurement.action_code != WLAN_ACTION_SPCT_MSR_RPRT) return false; return true; } static int __skw_cfg80211_mgmt_tx(struct wiphy *wiphy, struct skw_iface *iface, struct ieee80211_channel *chan, u32 wait, u64 *cookie, bool dont_wait_for_ack, const void *frame, int frame_len) { struct ieee80211_channel *tx_chan = chan; struct skw_core *skw = wiphy_priv(wiphy); int ret = 0; #define SKW_MGMT_TX_LEN 1500 if (!tx_chan) { if (is_skw_sta_mode(iface)) tx_chan = iface->sta.core.bss.channel; else tx_chan = iface->sap.cfg.channel; } skw_hex_dump("frame", frame, frame_len, false); down(&skw->cmd.mgmt_cmd_lock); if (!skw_cmd_data_len_in_limit(skw, frame_len + sizeof(struct skw_mgmt_tx_param))) { if (is_skw_rrm_report(frame, frame_len)) { int head_offset = offsetof(struct ieee80211_mgmt, u.action.u.measurement.element_id); int ret = -E2BIG; int elem_len = 0, next_len = 0; int left = frame_len - head_offset; char *pos = (u8 *)frame + head_offset, *next = pos; char *data = NULL; data = SKW_ZALLOC(SKW_MGMT_TX_LEN, GFP_KERNEL); if (!data) { skw_err("alloc %d failed\n", SKW_MGMT_TX_LEN); ret = -ENOMEM; goto unlock; } while (left) { int tx_len; next_len = next[1] + 2; tx_len = elem_len + head_offset + next_len; if (tx_len < SKW_MGMT_TX_LEN) { elem_len += next_len; left -= next_len; if (left) { next += next_len; continue; } } memcpy(data, frame, head_offset); memcpy(data + head_offset, pos, elem_len); skw_hex_dump("rrm", data, elem_len + head_offset, false); ret = skw_mgmt_tx(wiphy, iface, tx_chan, wait, cookie, dont_wait_for_ack, data, elem_len + head_offset, elem_len + head_offset, frame, true); pos = next; elem_len = 0; } SKW_KFREE(data); goto unlock; } else { int send_len; int remain = frame_len; const u8 *data = frame; int buf_size = SKW_MGMT_TX_LEN; while (remain > 0) { send_len = (remain < buf_size) ? remain : buf_size; remain -= send_len; skw_dbg("mgmt part : remain: %d, send_len: %d \n", remain, send_len); skw_hex_dump("part", data, send_len, false); ret = skw_mgmt_tx(wiphy, iface, tx_chan, wait, cookie, true, data, send_len, frame_len, frame, true); if (ret) { skw_err("failed, ret: %d\n", ret); goto unlock; } data += send_len; } if (dont_wait_for_ack == false) { struct skw_mgmt_status status; status.mgmt_status_data = SKW_ZALLOC(frame_len, GFP_KERNEL); if (!status.mgmt_status_data) { skw_err("malloc mgmt_status_data failed, size: %d\n", frame_len); ret = -ENOMEM; } else { status.mgmt_status_data_len = frame_len; memcpy(status.mgmt_status_data, frame, frame_len); status.mgmt_status_cookie = *cookie; skw_dbg("split part coockie:0x%llx\n", status.mgmt_status_cookie); skw_queue_work(wiphy, iface, SKW_WORK_SPLIT_MGMT_TX_STATUS, &status, sizeof(status)); } } goto unlock; } } #undef SKW_MGMT_TX_LEN skw_dump_frame((u8 *)frame, (u16)frame_len); ret = skw_mgmt_tx(wiphy, iface, tx_chan, wait, cookie, dont_wait_for_ack, frame, frame_len, frame_len, frame, true); unlock: up(&skw->cmd.mgmt_cmd_lock); return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) static int skw_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie) { struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); return __skw_cfg80211_mgmt_tx(wiphy, iface, params->chan, params->wait, cookie, params->dont_wait_for_ack, params->buf, params->len); } #else static int skw_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); return __skw_cfg80211_mgmt_tx(wiphy, iface, chan, wait, cookie, dont_wait_for_ack, buf, len); } #endif static int skw_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { int i; u8 *pos; struct cfg80211_bss *bss; struct ieee80211_mgmt *mgmt; struct ieee80211_supported_band *sband; struct skw_iface *iface = netdev_priv(dev); struct cfg80211_chan_def *chandef = ¶ms->chandef; skw_dbg("%s, bssid: %pM, ssid: %s, channel: %d, band: %u, chan_fixed: %d\n", netdev_name(dev), params->bssid, params->ssid, chandef->chan->hw_value, chandef->chan->band, params->channel_fixed); if (params->bssid) memcpy(iface->ibss.bssid, params->bssid, ETH_ALEN); else eth_random_addr(iface->ibss.bssid); iface->ibss.bw = to_skw_bw(params->chandef.width); if (iface->ibss.bw == SKW_CHAN_WIDTH_MAX) return -EINVAL; iface->ibss.beacon_int = params->beacon_interval; iface->ibss.channel = chandef->chan->hw_value; iface->ibss.band = to_skw_band(chandef->chan->band); iface->ibss.center_freq1 = chandef->center_freq1; iface->ibss.center_freq2 = chandef->center_freq2; iface->ibss.chandef = params->chandef; // start build presp frame mgmt = SKW_ZALLOC(SKW_2K_SIZE, GFP_KERNEL); if (!mgmt) { skw_err("malloc failed, size: %d\n", SKW_2K_SIZE); return -ENOMEM; } mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); eth_broadcast_addr(mgmt->da); memcpy(mgmt->sa, iface->addr, ETH_ALEN); memcpy(mgmt->bssid, iface->ibss.bssid, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(params->beacon_interval); // mgmt->u.beacon.timestamp = cpu_to_le64(0); mgmt->u.beacon.capab_info = cpu_to_le16(WLAN_CAPABILITY_IBSS); pos = mgmt->u.beacon.variable; *pos++ = WLAN_EID_SSID; *pos++ = params->ssid_len; memcpy(pos, params->ssid, params->ssid_len); pos += params->ssid_len; *pos++ = WLAN_EID_SUPP_RATES; *pos++ = 8; sband = wiphy->bands[chandef->chan->band]; for (i = 0; i < sband->n_bitrates; i++) { int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); *pos++ = rate | 0x80; } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) if (sband->band == IEEE80211_BAND_2GHZ) { #else if (sband->band == NL80211_BAND_2GHZ) { #endif *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = chandef->chan->hw_value; } *pos++ = WLAN_EID_IBSS_PARAMS; *pos++ = 2; *pos++ = 0; *pos++ = 0; #if 0 *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = 0; #endif if (params->ie) { memcpy(pos, params->ie, params->ie_len); pos += params->ie_len; } // end build frame // skw_set_template_frame(); bss = cfg80211_get_bss(wiphy, chandef->chan, params->bssid, params->ssid, params->ssid_len, SKW_BSS_TYPE_IBSS, SKW_PRIVACY_IBSS_ANY); if (!bss) { skw_info("creating new ibss: %pM\n", iface->ibss.bssid); bss = cfg80211_inform_bss_frame(wiphy, chandef->chan, mgmt, pos - (u8 *)mgmt, DBM_TO_MBM(-30), GFP_KERNEL); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) // fixme: if (params->wep_keys) { skw_add_key(wiphy, dev, 0, params->wep_tx_key, true, iface->ibss.bssid, params->wep_keys); skw_set_default_key(wiphy, dev, 0, params->wep_tx_key, true, true); } #endif cfg80211_put_bss(wiphy, bss); skw_queue_local_event(wiphy, iface, SKW_EVENT_LOCAL_IBSS_CONNECT, NULL, 0); SKW_KFREE(mgmt); return 0; } static int skw_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct skw_disconnect_param params = {0}; struct skw_iface *iface = netdev_priv(dev); skw_dbg("%s\n", netdev_name(dev)); iface->ibss.joined = false; iface->ibss.ssid_len = 0; params.type = SKW_DISCONNECT_ONLY; params.reason_code = 0; return skw_send_msg(wiphy, dev, SKW_CMD_DISCONNECT, ¶ms, sizeof(params), NULL, 0); } static int skw_set_wiphy_params(struct wiphy *wiphy, u32 changed) { int ret = 0; u16 *plen; struct skw_tlv_conf conf; skw_dbg("changed: 0x%x\n", changed); ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) return ret; plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_tlv_free(&conf); return -ENOMEM; } if (changed & WIPHY_PARAM_RETRY_SHORT) { if (skw_tlv_add(&conf, SKW_MIB_RETRY_SHORT, &wiphy->retry_short, sizeof(wiphy->retry_short))) skw_err("add SKW_MIB_RETRY_SHORT failed.\n"); } if (changed & WIPHY_PARAM_RETRY_LONG) { if (skw_tlv_add(&conf, SKW_MIB_RETRY_LONG, &wiphy->retry_long, sizeof(wiphy->retry_long))) skw_err("add SKW_MIB_RETRY_LONG failed.\n"); } if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { if (skw_tlv_add(&conf, SKW_MIB_FRAG_THRESHOLD, &wiphy->frag_threshold, sizeof(wiphy->frag_threshold))) skw_err("add SKW_MIB_FRAG_THRESHOLD failed.\n"); } if (changed & WIPHY_PARAM_RTS_THRESHOLD) { if (skw_tlv_add(&conf, SKW_MIB_RTS_THRESHOLD, &wiphy->rts_threshold, sizeof(wiphy->rts_threshold))) skw_err("add SKW_MIB_RTS_THRESHOLD failed.\n"); } if (conf.total_len) { *plen = conf.total_len; ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); } skw_tlv_free(&conf); return ret; } static int skw_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *req) { int i, ret; struct skw_scan_chan_info *chan = NULL; u32 delay = 0; u64 reqid = 0; s8 relative_rssi = 0; bool relative_rssi_set = false; s32 min_rssi_thold = 0; int n_scan_plans = 0, n_plans_len = 0; int n_ssids_len, n_match_len; int size, fixed, offset = 0; u16 scan_chn_num = 0; struct skw_sched_match_sets *match_sets; struct skw_core *skw = wiphy_priv(wiphy); struct skw_sched_scan_param *params; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) reqid = req->reqid; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) relative_rssi_set = req->relative_rssi_set; relative_rssi = req->relative_rssi; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) n_scan_plans = req->n_scan_plans; n_plans_len = n_scan_plans * sizeof(*req->scan_plans); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) delay = req->delay; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) min_rssi_thold = req->rssi_thold; #else min_rssi_thold = req->min_rssi_thold; #endif skw_dbg("%s, n_ssids: %d, n_channels: %d, n_match: %d, n_plans: %d, ie_len: %zd\n", netdev_name(dev), req->n_ssids, req->n_channels, req->n_match_sets, n_scan_plans, req->ie_len); fixed = sizeof(struct skw_sched_scan_param); n_ssids_len = req->n_ssids * sizeof(struct cfg80211_ssid); n_match_len = req->n_match_sets * sizeof(struct skw_sched_match_sets); size = fixed + req->ie_len + n_ssids_len + n_plans_len + n_match_len + req->n_channels * sizeof(*chan); params = SKW_ZALLOC(size, GFP_KERNEL); if (!params) { skw_err("malloc failed, size: %d\n", size); return -ENOMEM; } params->req_id = reqid; params->flags = req->flags; params->delay = delay; params->min_rssi_thold = min_rssi_thold; params->relative_rssi_set = relative_rssi_set; params->relative_rssi = relative_rssi; params->scan_width = NL80211_BSS_CHAN_WIDTH_20; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) skw_ether_copy(params->mac_addr, req->mac_addr); skw_ether_copy(params->mac_addr_mask, req->mac_addr_mask); #endif params->n_ssids = req->n_ssids; if (req->n_ssids) { params->n_ssid_offset = fixed + offset; params->n_ssids_len = n_ssids_len; memcpy(params->data + offset, req->ssids, n_ssids_len); offset += n_ssids_len; } match_sets = (void *)params->data + offset; for (i = 0; i < req->n_match_sets; i++) { memcpy(match_sets[i].ssid, req->match_sets[i].ssid.ssid, 32); match_sets[i].ssid_len = req->match_sets[i].ssid.ssid_len; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) match_sets[i].rssi_thold = req->match_sets[i].rssi_thold; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) skw_ether_copy(match_sets[i].bssid, req->match_sets[i].bssid); #endif } params->n_match_sets = req->n_match_sets; params->match_sets_offset = fixed + offset; params->match_sets_len = n_match_len; offset += n_match_len; params->n_scan_plans = n_scan_plans; if (n_scan_plans) { params->scan_plans_offset = fixed + offset; params->scan_plans_len = n_plans_len; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) memcpy(params->data + offset, req->scan_plans, n_plans_len); #endif offset += n_plans_len; } if (req->ie_len) { params->ie_offset = fixed + offset; params->ie_len = req->ie_len; memcpy(params->data + offset, req->ie, req->ie_len); offset += req->ie_len; } chan = (struct skw_scan_chan_info *)(params->data + offset); for (i = 0; i < req->n_channels; i++) { if (is_skw_6ghz_non_psc_chan(req->channels[i])) continue; chan->band = to_skw_band(req->channels[i]->band); chan->chan_num = req->channels[i]->hw_value; /* BIT[15]: set 1 means to run a passive scan on this channel */ if (req->channels[i]->flags & SKW_PASSIVE_SCAN) chan->scan_flags |= SKW_SCAN_FLAG_PASSIVE; chan++; scan_chn_num++; } params->n_channels = scan_chn_num; params->channels_len = scan_chn_num * sizeof(struct skw_scan_chan_info); params->channels_offset = fixed + offset; skw->sched_scan_req = req; ret = skw_send_msg(wiphy, dev, SKW_CMD_START_SCHED_SCAN, params, size, NULL, 0); if (ret) { skw_err("failed, ret: %d\n", ret); skw->sched_scan_req = NULL; } SKW_KFREE(params); return ret; } static int skw_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, u64 reqid) { u64 scan_id = 0; struct skw_core *skw = wiphy_priv(wiphy); skw_dbg("dev: %s, id: %lld, actived: %d\n", netdev_name(dev), scan_id, !!skw->sched_scan_req); if (!skw->sched_scan_req) return 0; skw->sched_scan_req = NULL; return skw_send_msg(wiphy, dev, SKW_CMD_STOP_SCHED_SCAN, &scan_id, sizeof(scan_id), NULL, 0); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) static int skw_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, u64 reqid) { return skw_sched_scan_stop(wiphy, dev, reqid); } #else static int skw_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) { return skw_sched_scan_stop(wiphy, dev, 0); } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) static void skw_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, struct mgmt_frame_regs *upd) { u64 ts; int ret = 0, idx; struct skw_mgmt_register_param param; struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); u16 new_mask = upd->interface_stypes; u16 temp_mask = new_mask ^ iface->mgmt_frame_bitmap; u16 frame_mtype; bool reg; if (!temp_mask) return; while (temp_mask) { idx = ffs(temp_mask) - 1; SKW_CLEAR(temp_mask, BIT(idx)); frame_mtype = idx << 4; reg = new_mask & BIT(idx); skw_dbg("%s %s filter %s\n", skw_iftype_name(wdev->iftype), reg ? "add" : "del", skw_mgmt_name(frame_mtype)); param.frame_type = frame_mtype; param.reg = reg; ts = local_clock(); do_div(ts, 1000000); param.timestamp = ts; ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_REGISTER_FRAME, ¶m, sizeof(param), NULL, 0); if (ret) { skw_err("%s %s failed, ret: %d\n", reg ? "add" : "del", skw_mgmt_name(frame_mtype), ret); } } iface->mgmt_frame_bitmap = new_mask; } #else static void skw_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg) { u64 ts = 0; int ret = 0; struct skw_mgmt_register_param param = {0}; int type = (frame_type >> 4) & 0xf; struct skw_iface *iface = SKW_WDEV_TO_IFACE(wdev); u16 bitmap = iface->mgmt_frame_bitmap; if (reg) iface->mgmt_frame_bitmap |= BIT(type); else iface->mgmt_frame_bitmap &= ~BIT(type); if (bitmap == iface->mgmt_frame_bitmap) return; skw_dbg("%s %s filter %s\n", skw_iftype_name(wdev->iftype), reg ? "add" : "del", skw_mgmt_name(frame_type)); param.frame_type = frame_type; param.reg = reg; ts = local_clock(); do_div(ts, 1000000); param.timestamp = ts; ret = skw_msg_xmit(wiphy, iface->id, SKW_CMD_REGISTER_FRAME, ¶m, sizeof(param), NULL, 0); if (ret) { skw_err("%s %s failed, ret: %d\n", reg ? "add" : "del", skw_mgmt_name(frame_type), ret); } } #endif static int skw_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { /* firmware trigger legacy ps automatically */ skw_dbg("%s, enabled: %d, timeout: %d\n", netdev_name(dev), enabled, timeout); return 0; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) static int skw_set_qos_map(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_qos_map *qos_map) { struct skw_iface *iface = netdev_priv(dev); skw_dbg("ndev: %s, %s qos_map\n", netdev_name(dev), qos_map ? "add" : "del"); if (!qos_map) { SKW_KFREE(iface->qos_map); return 0; } if (!iface->qos_map) { iface->qos_map = SKW_ZALLOC(sizeof(struct cfg80211_qos_map), GFP_KERNEL); if (!iface->qos_map) return -ENOMEM; } memcpy(iface->qos_map, qos_map, sizeof(*qos_map)); return 0; } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) static int skw_add_tx_ts(struct wiphy *wiphy, struct net_device *ndev, u8 tsid, const u8 *peer, u8 up, u16 admitted_time) { struct skw_ts_info ts; skw_dbg("dev: %s, ts id: %d, addr: %pM, up: %d, time: %d\n", netdev_name(ndev), tsid, peer, up, admitted_time); /* cfg80211 will make a sanity check */ ts.up = up; ts.tsid = tsid; skw_ether_copy(ts.peer, peer); ts.admitted_time = admitted_time; return skw_send_msg(wiphy, ndev, SKW_CMD_ADD_TX_TS, &ts, sizeof(ts), NULL, 0); } static int skw_del_tx_ts(struct wiphy *wiphy, struct net_device *ndev, u8 tsid, const u8 *peer) { struct skw_ts_info ts; skw_dbg("dev: %s, ts id: %d, addr: %pM\n", netdev_name(ndev), tsid, peer); ts.tsid = tsid; skw_ether_copy(ts.peer, peer); ts.up = 0xFF; ts.admitted_time = 0; return skw_send_msg(wiphy, ndev, SKW_CMD_DEL_TX_TS, &ts, sizeof(ts), NULL, 0); } #endif static int skw_tdls_oper(struct wiphy *wiphy, struct net_device *ndev, const u8 *peer_addr, enum nl80211_tdls_operation oper) { int ret = 0; struct skw_iface *iface = netdev_priv(ndev); struct skw_tdls_oper tdls; struct skw_peer_ctx *ctx; skw_dbg("dev: %s, oper: %d, addr: %pM\n", netdev_name(ndev), oper, peer_addr); ctx = skw_peer_ctx(iface, peer_addr); if (!ctx) return -ENOENT; switch (oper) { case NL80211_TDLS_ENABLE_LINK: skw_peer_ctx_transmit(ctx, true); break; case NL80211_TDLS_DISABLE_LINK: skw_peer_ctx_transmit(ctx, false); skw_peer_ctx_bind(iface, ctx, NULL); break; default: ret = -ENOTSUPP; break; } if (ret) return ret; tdls.oper = oper; skw_ether_copy(tdls.peer_addr, peer_addr); return skw_send_msg(wiphy, ndev, SKW_CMD_TDLS_OPER, &tdls, sizeof(tdls), NULL, 0); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) static int skw_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *ndev, const u8 *peer_addr, enum nl80211_tdls_operation oper) { return skw_tdls_oper(wiphy, ndev, peer_addr, oper); } #else static int skw_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *ndev, u8 *peer_addr, enum nl80211_tdls_operation oper) { return skw_tdls_oper(wiphy, ndev, (const u8 *)peer_addr, oper); } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) static int skw_tdls_chn_switch(struct wiphy *wiphy, struct net_device *ndev, const u8 *addr, u8 oper_class, struct cfg80211_chan_def *def) { int ret; struct skw_tdls_chan_switch tdls; struct skw_peer_ctx *ctx; struct skw_iface *iface = netdev_priv(ndev); skw_dbg("dev: %s, addr: %pM, def chan: %d\n", netdev_name(ndev), addr, def->chan->hw_value); ctx = skw_peer_ctx(iface, addr); if (!ctx) { skw_err("can't find tdls peer: %pM\n", addr); return -EINVAL; } if (!skw_channel_allowed(wiphy, def->chan->hw_value)) return -EBUSY; switch (def->width) { case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: tdls.chan_width = SKW_CHAN_WIDTH_20; break; case NL80211_CHAN_WIDTH_40: tdls.chan_width = SKW_CHAN_WIDTH_40; break; case NL80211_CHAN_WIDTH_80: tdls.chan_width = SKW_CHAN_WIDTH_80; break; default: skw_err("channel width: %d not support\n", def->width); return -ENOTSUPP; } skw_ether_copy(tdls.addr, addr); tdls.chn_switch_enable = 1; tdls.oper_class = oper_class; tdls.chn = def->chan->hw_value; tdls.band = to_skw_band(def->chan->band); ret = skw_send_msg(wiphy, ndev, SKW_CMD_TDLS_CHANNEL_SWITCH, &tdls, sizeof(tdls), NULL, 0); if (!ret) { skw_peer_ctx_lock(ctx); if (ctx->peer) ctx->peer->channel = def->chan->hw_value; skw_peer_ctx_unlock(ctx); } return ret; } static void skw_tdls_cancel_chn_switch(struct wiphy *wiphy, struct net_device *ndev, const u8 *addr) { struct skw_tdls_chan_switch tdls; struct skw_iface *iface = netdev_priv(ndev); skw_dbg("dev: %s, addr: %pM\n", netdev_name(ndev), addr); if (!skw_peer_ctx(iface, addr)) { skw_dbg("can't find tdls peer:%pM\n", addr); return; } memset(&tdls, 0x0, sizeof(tdls)); tdls.chn_switch_enable = 0; skw_ether_copy(tdls.addr, addr); if (skw_send_msg(wiphy, ndev, SKW_CMD_TDLS_CHANNEL_SWITCH, &tdls, sizeof(tdls), NULL, 0) < 0) skw_err("set command SKW_CMD_TDLS_CANCEL_CHN_SWITCH failed\n"); } #endif static int skw_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action, u8 token, u16 status, u32 peer_capability, bool initiator, const u8 *ies, size_t ies_len) { struct skw_core *skw = wiphy_priv(wiphy); return skw_tdls_build_send_mgmt(skw, dev, peer, action, token, status, peer_capability, initiator, ies, ies_len); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) static int skw_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action, u8 token, u16 status, u32 peer_capability, bool initiator, const u8 *ies, size_t ies_len) { return skw_tdls_mgmt(wiphy, dev, peer, action, token, status, peer_capability, initiator, ies, ies_len); } #else static int skw_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) const u8 *peer, u8 action, u8 token, u16 status, #else u8 *peer, u8 action, u8 token, u16 status, #endif u32 peer_capability, const u8 *ies, size_t ies_len) { return skw_tdls_mgmt(wiphy, dev, (const u8 *)peer, action, token, status, peer_capability, false, ies, ies_len); } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) //iw phy5 wowlan enable patterns 28+43:34:-:12 16+33:-:11:ee:12:34:-:88:99 static int skw_wow_enable(struct wiphy *wiphy) { int ret = 0; #ifdef CONFIG_PM struct cfg80211_wowlan *wow = wiphy->wowlan_config; struct cfg80211_pkt_pattern *patterns = wow->patterns; u32 i, j; int total; struct skw_spd_action_param *spd = NULL; struct skw_wow_input_param *wow_param = NULL; struct skw_wow_rule *rule; struct skw_pkt_pattern *ptn; struct skw_pkt_pattern ptn_tmp; int vi = 0; int y, b, start = 0, gap = 0; u8 *rdata; total = sizeof(struct skw_spd_action_param) + sizeof(struct skw_wow_input_param); if (wow->any) { spd = SKW_ZALLOC(total, GFP_KERNEL); if (!spd) { skw_err("malloc failed, size: %d\n", total); return -ENOMEM; } wow_param = (struct skw_wow_input_param *)((u8 *)spd + sizeof(*spd)); wow_param->wow_flags = SKW_WOW_ANY_PKT; wow_param->rule_num = 0; spd->sub_cmd = ACTION_EN_WOW; spd->len = sizeof(struct skw_wow_input_param); goto cmd_send; } total += sizeof(struct skw_wow_rule) * wow->n_patterns; spd = SKW_ZALLOC(total, GFP_KERNEL); if (!spd) { skw_err("malloc failed, size: %d\n", total); return -ENOMEM; } wow_param = (struct skw_wow_input_param *)((u8 *)spd + sizeof(*spd)); wow_param->rule_num = wow->n_patterns; spd->sub_cmd = ACTION_EN_WOW; if (wow->disconnect) wow_param->wow_flags |= SKW_WOW_DISCONNECT; if (wow->magic_pkt) wow_param->wow_flags |= SKW_WOW_MAGIC_PKT; if (wow->gtk_rekey_failure) wow_param->wow_flags |= SKW_WOW_GTK_REKEY_FAIL; if (wow->eap_identity_req) wow_param->wow_flags |= SKW_WOW_EAP_IDENTITY_REQ; if (wow->four_way_handshake) wow_param->wow_flags |= SKW_WOW_FOUR_WAY_HANDSHAKE; if (wow->rfkill_release) wow_param->wow_flags |= SKW_WOW_RFKILL_RELEASE; for (i = 0; i < wow_param->rule_num; i++) { rule = &wow_param->rules[i]; rdata = rule->rule; ptn_tmp.op = PAT_OP_TYPE_SAME; ptn_tmp.type_offset = PAT_TYPE_ETH; ptn_tmp.offset = patterns[i].pkt_offset; ptn_tmp.len = 0; vi = 0; start = 0; gap = 0; for (j = 0; j < patterns[i].pattern_len; j++) { y = round_up(j + 1, 8)/8 - 1; b = j%8; if (patterns[i].mask[y] & BIT(b)) { if (!start) { if (vi + sizeof(ptn_tmp) >= sizeof(rule->rule)) { skw_warn("pat:%d overage\n", i); break; } ptn = (struct skw_pkt_pattern *)&rdata[vi]; memcpy(ptn, &ptn_tmp, sizeof(ptn_tmp)); ptn->offset += gap; vi += sizeof(ptn_tmp); } rdata[vi++] = patterns[i].pattern[j]; ptn->len++; start = 1; gap++; if (vi >= sizeof(rule->rule)) { skw_warn("pat:%d overage\n", i); break; } } else { gap++; start = 0; } } rule->len = vi; skw_hex_dump("rule", rule, sizeof(*rule), false); } spd->len = sizeof(struct skw_wow_input_param) + sizeof(struct skw_wow_rule) * wow_param->rule_num; cmd_send: skw_dbg("len:%d %d\n", spd->len, total); skw_hex_dump("wow", spd, total, false); ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_SPD_ACTION, spd, total, NULL, 0); if (ret) skw_err("failed, ret: %d\n", ret); SKW_KFREE(spd); #endif return ret; } #endif int skw_wow_disable(struct wiphy *wiphy) { struct skw_spd_action_param spd; int ret = 0; spd.sub_cmd = ACTION_DIS_WOW; spd.len = 0; ret = skw_msg_xmit(wiphy, 0, SKW_CMD_SET_SPD_ACTION, &spd, sizeof(spd), NULL, 0); if (ret) skw_err("failed, ret: %d\n", ret); return ret; } int skw_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) { int ret; unsigned long flags; struct skw_suspend_t suspend; struct skw_core *skw = wiphy_priv(wiphy); skw_dbg("WoW: %s, skw flags: 0x%lx\n", wow ? "enabled" : "disabled", skw->flags); /* * If we have transmitted packets, but don't receive the txc on pcie * platform, just return busy, because hw can't access the host * memory while host is sleep. */ if (skw->hw.bus == SKW_BUS_PCIE && !skw_edma_is_txc_completed(skw)) { skw_dbg("txc is not completed"); return -EBUSY; } set_bit(SKW_FLAG_BLOCK_TX, &skw->flags); memset(&suspend, 0x0, sizeof(suspend)); /* WoW disabled */ if (!wow) { suspend.wow_enable = 0; goto send; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) if (wow->nd_config) skw_cfg80211_sched_scan_start(wiphy, wow->nd_config->dev, wow->nd_config); #endif suspend.wow_enable = 1; if (wow->disconnect) suspend.wow_flags |= SKW_WOW_DISCONNECT; if (wow->magic_pkt) suspend.wow_flags |= SKW_WOW_MAGIC_PKT; if (wow->gtk_rekey_failure) suspend.wow_flags |= SKW_WOW_GTK_REKEY_FAIL; if (wow->eap_identity_req) suspend.wow_flags |= SKW_WOW_EAP_IDENTITY_REQ; if (wow->four_way_handshake) suspend.wow_flags |= SKW_WOW_FOUR_WAY_HANDSHAKE; if (wow->rfkill_release) suspend.wow_flags |= SKW_WOW_RFKILL_RELEASE; send: flags = BIT(SKW_CMD_FLAG_IGNORE_BLOCK_TX) | BIT(SKW_CMD_FLAG_NO_ACK) | BIT(SKW_CMD_FLAG_NO_WAKELOCK); if (skw->hw.bus == SKW_BUS_SDIO) flags |= BIT(SKW_CMD_FLAG_DISABLE_IRQ); ret = skw_msg_xmit_timeout(wiphy, 0, SKW_CMD_SUSPEND, &suspend, sizeof(suspend), NULL, 0, "SKW_CMD_SUSPEND", msecs_to_jiffies(SKW_CMD_TIMEOUT), flags); if (ret) { clear_bit(SKW_FLAG_BLOCK_TX, &skw->flags); skw_err("ret: %d, fw flags: 0x%lx\n", ret, skw->flags); } return ret; } static int skw_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) { struct skw_core *skw = wiphy_priv(wiphy); skw_dbg("bus: %s, hw flags: 0x%lx\n", skw_bus_name(skw->hw.bus), skw->hw.flags); if (test_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags)) return skw_suspend(wiphy, wow); if (!wow) { skw->hw.wow.flags = 0; skw->hw.wow.enabled = false; return 0; } if (wow->disconnect) skw->hw.wow.flags |= SKW_WOW_DISCONNECT; if (wow->magic_pkt) skw->hw.wow.flags |= SKW_WOW_MAGIC_PKT; if (wow->gtk_rekey_failure) skw->hw.wow.flags |= SKW_WOW_GTK_REKEY_FAIL; if (wow->eap_identity_req) skw->hw.wow.flags |= SKW_WOW_EAP_IDENTITY_REQ; if (wow->four_way_handshake) skw->hw.wow.flags |= SKW_WOW_FOUR_WAY_HANDSHAKE; if (wow->rfkill_release) skw->hw.wow.flags |= SKW_WOW_RFKILL_RELEASE; skw->hw.wow.enabled = true; return 0; } int skw_resume(struct wiphy *wiphy) { int ret = 0; struct skw_core *skw = wiphy_priv(wiphy); skw_dbg("skw flags: 0x%lx\n", skw->flags); clear_bit(SKW_FLAG_BLOCK_TX, &skw->flags); ret = skw_msg_xmit(wiphy, 0, SKW_CMD_RESUME, NULL, 0, NULL, 0); if (ret) skw_warn("ret: %d\n", ret); return 0; } static int skw_cfg80211_resume(struct wiphy *wiphy) { struct skw_core *skw = wiphy_priv(wiphy); skw_dbg("bus: %s, hw flags: 0x%lx\n", skw_bus_name(skw->hw.bus), skw->hw.flags); if (test_bit(SKW_HW_FLAG_CFG80211_PM, &skw->hw.flags)) return skw_resume(wiphy); return 0; } static void skw_set_wakeup(struct wiphy *wiphy, bool enabled) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) if (enabled) skw_wow_enable(wiphy); else skw_wow_disable(wiphy); #endif device_set_wakeup_enable(wiphy_dev(wiphy), enabled); } static int skw_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) { skw_dbg("traced\n"); return 0; } static void skw_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev) { skw_dbg("traced\n"); } static int skw_probe_client(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie) { skw_dbg("traced\n"); return 0; } static int skw_change_bss(struct wiphy *wiphy, struct net_device *ndev, struct bss_parameters *params) { struct skw_iface *iface = netdev_priv(ndev); skw_dbg("%s ap_isolate:%d\n", netdev_name(ndev), params->ap_isolate); if (params->ap_isolate >= 0) iface->sap.ap_isolate = params->ap_isolate; return 0; } static int skw_set_monitor_channel(struct wiphy *wiphy, struct cfg80211_chan_def *chandef) { return skw_cmd_monitor(wiphy, chandef, SKW_MONITOR_COMMON); } static int skw_dump_survey(struct wiphy *wiphy, struct net_device *ndev, int idx, struct survey_info *info) { struct skw_iface *iface = netdev_priv(ndev); struct skw_survey_info *sinfo = NULL; int freq; skw_detail("%s, idx: %d\n", netdev_name(ndev), idx); sinfo = list_first_entry_or_null(&iface->survey_list, struct skw_survey_info, list); if (!sinfo) { skw_dbg("last idx: %d\n", idx); return -EINVAL; } list_del(&sinfo->list); freq = ieee80211_channel_to_frequency(sinfo->data.chan, to_nl80211_band(sinfo->data.band)); info->noise = sinfo->data.noise; info->channel = ieee80211_get_channel(wiphy, freq); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) info->time = sinfo->data.time; info->time_busy = sinfo->data.time_busy; info->time_ext_busy = sinfo->data.time_ext_busy; info->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_EXT_BUSY | SURVEY_INFO_NOISE_DBM; #else info->channel_time = sinfo->data.time; info->channel_time_busy = sinfo->data.time_busy; info->channel_time_ext_busy = sinfo->data.time_ext_busy; info->filled = SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_EXT_BUSY | SURVEY_INFO_NOISE_DBM; #endif SKW_KFREE(sinfo); return 0; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) static int skw_external_auth(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_external_auth_params *params) { struct skw_iface *iface = netdev_priv(ndev); skw_dbg("%s bssid: %pM, action: %u, status: %u\n", netdev_name(ndev), params->bssid, params->action, params->status); if (iface->wdev.iftype == NL80211_IFTYPE_AP || iface->wdev.iftype == NL80211_IFTYPE_P2P_GO) { return 0; } /* Non-AP STA */ if (!iface->sta.conn) { skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); return -EINVAL; } if (params->status != WLAN_STATUS_SUCCESS) { skw_set_state(&iface->sta.core.sm, SKW_STATE_NONE); skw_unjoin(wiphy, ndev, params->bssid, SKW_LEAVE, false); // release peer and report connect result cfg80211_connect_result(iface->ndev, params->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); return 0; } skw_set_state(&iface->sta.core.sm, SKW_STATE_AUTHED); return skw_connect_assoc(wiphy, ndev, iface->sta.conn); } #endif static int skw_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_update_ft_ies_params *ftie) { #if 0 struct skw_iface *iface = NULL; struct cfg80211_assoc_request req; u8 *ie = NULL; int ret = 0; skw_dbg("md:%u\n", ftie->md); if (ftie->ie && ftie->ie_len) { iface->sta.ft_ie = SKW_ZALLOC(ftie->ie_len, GFP_KERNEL); if (iface->sta.ft_ie) memcpy(iface->sta.ft_ie, ftie->ie, ftie->ie_len); iface->sta.ft_ie_len = ftie->ie_len; skw_dbg("ft ie len:%u\n", iface->sta.ft_ie_len); } skw_dbg("state:%u\n", iface->sta.core.sm.state); if (iface->sta.core.sm.state != SKW_STATE_AUTHING) { skw_dbg("received update ft cmd during EAPOL process\n"); return 0; } // req.bss = iface->sta.associating_bss; req.ie_len = iface->sta.assoc_ie_len + ftie->ie_len; ie = SKW_ZALLOC(req.ie_len, GFP_KERNEL); if (!ie) { skw_err("Mem is not enough\n"); return -ENOMEM; } memcpy(ie, ftie->ie, ftie->ie_len); memcpy(ie + ftie->ie_len, iface->sta.assoc_ie, iface->sta.assoc_ie_len); req.ie = ie; req.prev_bssid = iface->sta.core.bssid; req.use_mfp = iface->sta.use_mfp; req.flags = iface->sta.flags; req.ht_capa = iface->sta.ht_capa; req.ht_capa_mask = iface->sta.ht_capa_mask; req.vht_capa = iface->sta.vht_capa; req.vht_capa_mask = iface->sta.vht_capa_mask; ret = skw_assoc(iface->wdev.wiphy, iface->ndev, &req); SKW_KFREE(ie); return ret; #endif return 0; } static int skw_start_radar_detection(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_chan_def *chandef, u32 cac_time_ms) { int ret; struct skw_core *skw = wiphy_priv(wiphy); struct skw_iface *iface = netdev_priv(dev); skw_dbg("dev: %s, channel: %d, cac time: %dms, dfs_region: %d, fw enabled: %d\n", netdev_name(dev), chandef->chan->hw_value, cac_time_ms, skw->dfs.region, skw->dfs.fw_enabled); if (!skw->dfs.fw_enabled) return -EINVAL; ret = skw_dfs_chan_init(wiphy, dev, chandef, cac_time_ms); if (ret) return ret; ret = skw_dfs_start_cac(wiphy, dev); if (!ret) { set_bit(SKW_DFS_FLAG_CAC_MODE, &iface->sap.dfs.flags); queue_delayed_work(skw->event_wq, &iface->sap.dfs.cac_work, msecs_to_jiffies(cac_time_ms)); } return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) static int skw_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *csa) { int ret; char *buff; const u8 *ie; int ie_len, offset = 0; skw_dbg("dev: %s, chan: %d, width: %d\n", netdev_name(dev), csa->chandef.chan->hw_value, csa->chandef.width); buff = SKW_ZALLOC(csa->beacon_csa.tail_len, GFP_KERNEL); if (!buff) return -ENOMEM; ie = cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, csa->beacon_csa.tail, csa->beacon_csa.tail_len); if (ie) { ie_len = ie[1] + 2; memcpy(buff, ie, ie_len); offset = ie_len; } ie = cfg80211_find_ie(WLAN_EID_EXT_CHANSWITCH_ANN, csa->beacon_csa.tail, csa->beacon_csa.tail_len); if (ie) { ie_len = ie[1] + 2; memcpy(buff + offset, ie, ie_len); offset += ie_len; } ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, csa->beacon_csa.tail, csa->beacon_csa.tail_len); if (ie) { ie_len = ie[1] + 2; memcpy(buff + offset, ie, ie_len); offset += ie_len; } ie = cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH_WRAPPER, csa->beacon_csa.tail, csa->beacon_csa.tail_len); if (ie) { ie_len = ie[1] + 2; memcpy(buff + offset, ie, ie_len); offset += ie_len; } ret = skw_send_msg(wiphy, dev, SKW_CMD_REQ_CHAN_SWITCH, buff, offset, NULL, 0); SKW_KFREE(buff); return ret; } #endif #ifdef __SKW_ANDROID__ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 74) static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *params)) { return skw_get_key(wiphy, dev, link_id, key_idx, pairwise, mac_addr, cookie, callback); } static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *addr, struct key_params *params) { return skw_add_key(wiphy, dev, link_id, key_idx, pairwise, addr, params); } static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *addr) { return skw_del_key(wiphy, dev, link_id, key_idx, pairwise, addr); } static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool unicast, bool multicast) { return skw_set_default_key(wiphy, dev, link_id, key_idx, unicast, multicast); } static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx) { return skw_set_default_mgmt_key(wiphy, dev, link_id, key_idx); } static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req) { bool tx = !req->local_state_change; const u8 *bssid = req->ap_addr; return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); } #else static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *params)) { return skw_get_key(wiphy, dev, 0, key_idx, pairwise, mac_addr, cookie, callback); } static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *addr, struct key_params *params) { return skw_add_key(wiphy, dev, 0, key_idx, pairwise, addr, params); } static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *addr) { return skw_del_key(wiphy, dev, 0, key_idx, pairwise, addr); } static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool unicast, bool multicast) { return skw_set_default_key(wiphy, dev, 0, key_idx, unicast, multicast); } static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx) { return skw_set_default_mgmt_key(wiphy, dev, 0, key_idx); } static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req) { bool tx = !req->local_state_change; const u8 *bssid = (const u8 *)req->bss->bssid; return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 41) static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id) { return skw_stop_ap(wiphy, dev, link_id); } #else static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { return skw_stop_ap(wiphy, dev, 0); } #endif #else /* Linux */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *params)) { return skw_get_key(wiphy, dev, link_id, key_idx, pairwise, mac_addr, cookie, callback); } static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *addr, struct key_params *params) { return skw_add_key(wiphy, dev, link_id, key_idx, pairwise, addr, params); } static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool pairwise, const u8 *addr) { return skw_del_key(wiphy, dev, link_id, key_idx, pairwise, addr); } static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx, bool unicast, bool multicast) { return skw_set_default_key(wiphy, dev, link_id, key_idx, unicast, multicast); } static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, int link_id, u8 key_idx) { return skw_set_default_mgmt_key(wiphy, dev, link_id, key_idx); } static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req) { bool tx = !req->local_state_change; const u8 *bssid = req->ap_addr; return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); } static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id) { return skw_stop_ap(wiphy, dev, link_id); } #else static int skw_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *params)) { return skw_get_key(wiphy, dev, 0, key_idx, pairwise, mac_addr, cookie, callback); } static int skw_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *addr, struct key_params *params) { return skw_add_key(wiphy, dev, 0, key_idx, pairwise, addr, params); } static int skw_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *addr) { return skw_del_key(wiphy, dev, 0, key_idx, pairwise, addr); } static int skw_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool unicast, bool multicast) { return skw_set_default_key(wiphy, dev, 0, key_idx, unicast, multicast); } static int skw_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx) { return skw_set_default_mgmt_key(wiphy, dev, 0, key_idx); } static int skw_cfg80211_disassoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_disassoc_request *req) { bool tx = !req->local_state_change; const u8 *bssid = (const u8 *)req->bss->bssid; return skw_disassoc(wiphy, dev, bssid, req->reason_code, tx); } static int skw_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { return skw_stop_ap(wiphy, dev, 0); } #endif #endif /* Linux */ static struct cfg80211_ops skw_cfg80211_ops = { .add_virtual_intf = skw_cfg80211_add_virtual_intf, .del_virtual_intf = skw_cfg80211_del_virtual_intf, .change_virtual_intf = skw_cfg80211_change_intf, .scan = skw_scan, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) .abort_scan = skw_abort_scan, #endif .get_key = skw_cfg80211_get_key, .add_key = skw_cfg80211_add_key, .del_key = skw_cfg80211_del_key, .set_default_key = skw_cfg80211_set_default_key, .set_default_mgmt_key = skw_cfg80211_set_default_mgmt_key, .change_beacon = skw_change_beacon, .start_ap = skw_cfg80211_start_ap, .stop_ap = skw_cfg80211_stop_ap, .add_station = skw_cfg80211_add_station, .change_station = skw_cfg80211_change_station, .del_station = skw_cfg80211_del_station, .get_station = skw_cfg80211_get_station, .auth = skw_cfg80211_auth, .assoc = skw_cfg80211_assoc, .deauth = skw_cfg80211_deauth, .disassoc = skw_cfg80211_disassoc, .connect = skw_cfg80211_connect, .disconnect = skw_cfg80211_disconnect, .join_ibss = skw_join_ibss, .leave_ibss = skw_leave_ibss, .set_wiphy_params = skw_set_wiphy_params, .remain_on_channel = skw_remain_on_channel, .cancel_remain_on_channel = skw_cancel_roc, .mgmt_tx = skw_cfg80211_mgmt_tx, .sched_scan_start = skw_cfg80211_sched_scan_start, .sched_scan_stop = skw_cfg80211_sched_scan_stop, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) .update_mgmt_frame_registrations = skw_mgmt_frame_register, #else .mgmt_frame_register = skw_mgmt_frame_register, #endif .set_power_mgmt = skw_set_power_mgmt, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) .set_cqm_rssi_config = skw_set_cqm_rssi_config, .set_cqm_rssi_range_config = skw_set_cqm_rssi_range_config, #endif .start_p2p_device = skw_start_p2p_device, .stop_p2p_device = skw_stop_p2p_device, .set_mac_acl = skw_set_mac_acl, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) .set_qos_map = skw_set_qos_map, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) .add_tx_ts = skw_add_tx_ts, .del_tx_ts = skw_del_tx_ts, #endif .tdls_mgmt = skw_cfg80211_tdls_mgmt, .tdls_oper = skw_cfg80211_tdls_oper, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) .tdls_channel_switch = skw_tdls_chn_switch, .tdls_cancel_channel_switch = skw_tdls_cancel_chn_switch, #endif .suspend = skw_cfg80211_suspend, .resume = skw_cfg80211_resume, .set_wakeup = skw_set_wakeup, .probe_client = skw_probe_client, .dump_survey = skw_dump_survey, .set_monitor_channel = skw_set_monitor_channel, .change_bss = skw_change_bss, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) .external_auth = skw_external_auth, #endif .update_ft_ies = skw_update_ft_ies, .start_radar_detection = skw_start_radar_detection, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) .channel_switch = skw_channel_switch, #endif }; static void skw_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req) { struct skw_core *skw = wiphy_priv(wiphy); skw_info("regd: %s, initiator = %d, dfs_region: %d\n", req->alpha2, req->initiator, req->dfs_region); skw->dfs.region = req->dfs_region; if (!skw_set_wiphy_regd(wiphy, req->alpha2)) skw_cmd_set_regdom(wiphy, req->alpha2); } struct wiphy *skw_alloc_wiphy(int priv_size) { #ifdef CONFIG_SWT6621S_STA_SME_EXT skw_cfg80211_ops.connect = NULL; skw_cfg80211_ops.disconnect = NULL; #else skw_cfg80211_ops.auth = NULL; skw_cfg80211_ops.assoc = NULL; skw_cfg80211_ops.deauth = NULL; skw_cfg80211_ops.disassoc = NULL; #endif return wiphy_new(&skw_cfg80211_ops, priv_size); } #ifdef CONFIG_PM /* cfg80211 wowlan definitions */ #define SKW_WOWLAN_MAX_PATTERNS 15 #define SKW_WOWLAN_MIN_PATTERN_LEN 1 #define SKW_WOWLAN_MAX_PATTERN_LEN 255 #define SKW_WOWLAN_PKT_FILTER_ID_FIRST 201 static const struct wiphy_wowlan_support skw_wowlan_support = { .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_MAGIC_PKT, .n_patterns = SKW_WOWLAN_MAX_PATTERNS, .pattern_min_len = SKW_WOWLAN_MIN_PATTERN_LEN, .pattern_max_len = SKW_WOWLAN_MAX_PATTERN_LEN, .max_pkt_offset = SKW_WOWLAN_MAX_PATTERN_LEN, }; #endif /* CONFIG_PM */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) struct skw_iftype_ext_cap iftype_ext_cap[NUM_NL80211_IFTYPES] = { {NL80211_IFTYPE_STATION, {0}, 0}, {NL80211_IFTYPE_AP, {0}, 0}, {NL80211_IFTYPE_P2P_GO, {0}, 0}, #ifndef CONFIG_SWT6621S_LEGACY_P2P {NL80211_IFTYPE_P2P_DEVICE, {0}, 0}, #endif }; static struct skw_iftype_ext_cap *skw_get_iftype_ext_cap(u8 iftype) { int i; struct skw_iftype_ext_cap *capab = NULL; for (i = 0; i < NUM_NL80211_IFTYPES; i++) { if (iftype_ext_cap[i].iftype == iftype) capab = &iftype_ext_cap[iftype]; } return capab; } static void skw_setup_wiphy_iftype_ext_cap(struct wiphy *wiphy) { struct skw_core *skw = wiphy_priv(wiphy); struct wiphy_iftype_ext_capab *capab = NULL; struct skw_iftype_ext_cap *skw_ext_cap = NULL; skw->num_iftype_ext_capab = 0; if (wiphy->interface_modes & (BIT(NL80211_IFTYPE_STATION))) { capab = &skw->iftype_ext_cap[NL80211_IFTYPE_STATION]; capab->iftype = NL80211_IFTYPE_STATION; skw_ext_cap = skw_get_iftype_ext_cap(capab->iftype); capab->extended_capabilities = skw_ext_cap->ext_cap; capab->extended_capabilities_mask = skw_ext_cap->ext_cap; capab->extended_capabilities_len = skw_ext_cap->ext_cap_len; skw->num_iftype_ext_capab++; } if (wiphy->interface_modes & (BIT(NL80211_IFTYPE_AP))) { capab = &skw->iftype_ext_cap[NL80211_IFTYPE_AP]; capab->iftype = NL80211_IFTYPE_AP; skw_ext_cap = skw_get_iftype_ext_cap(capab->iftype); capab->extended_capabilities = skw_ext_cap->ext_cap; capab->extended_capabilities_mask = skw_ext_cap->ext_cap; capab->extended_capabilities_len = skw_ext_cap->ext_cap_len; skw->num_iftype_ext_capab++; } skw->num_iftype_ext_capab = 0; //Remove it after set the actual info wiphy->num_iftype_ext_capab = skw->num_iftype_ext_capab; wiphy->iftype_ext_capab = skw->iftype_ext_cap; } #endif static void skw_sync_band_capa(struct ieee80211_supported_band *band, struct skw_chip_info *chip) { u32 flags; u16 bit_rate; int i, mcs_map; int tx_chain = 0, rx_chain = 0; band->ht_cap.cap = chip->ht_capa; band->ht_cap.ht_supported = true; band->ht_cap.ampdu_factor = chip->ht_ampdu_param & 0x3; band->ht_cap.ampdu_density = (chip->ht_ampdu_param >> 2) & 0x7; for (i = 0; i < 4; i++) { mcs_map = (chip->ht_rx_mcs_maps >> (i * 8)) & 0xff; if (mcs_map) { rx_chain++; band->ht_cap.mcs.rx_mask[i] = mcs_map; } mcs_map = (chip->ht_tx_mcs_maps >> (i * 8)) & 0xff; if (mcs_map) tx_chain++; } if (chip->fw_bw_capa & SKW_BW_2GHZ_40M) bit_rate = rx_chain * 150; /* Mbps */ else bit_rate = rx_chain * 72; /* Mbps */ band->ht_cap.mcs.rx_highest = cpu_to_le16(bit_rate); band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; if (tx_chain != rx_chain) { band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_RX_DIFF; band->ht_cap.mcs.tx_params |= ((tx_chain - 1) << 2); } band->vht_cap.cap = chip->vht_capa; band->vht_cap.vht_supported = true; band->vht_cap.vht_mcs.tx_mcs_map = chip->vht_tx_mcs_maps; band->vht_cap.vht_mcs.rx_mcs_map = chip->vht_rx_mcs_maps; if (!chip->fw_bw_capa) return; /* set channel flags */ for (flags = 0, i = 0; i < 32; i++) { if (!(chip->fw_bw_capa & BIT(i))) { switch (BIT(i)) { case SKW_BW_CAP_2G_20M: case SKW_BW_CAP_5G_20M: flags |= SKW_IEEE80211_CHAN_NO_20MHZ; break; case SKW_BW_CAP_2G_40M: case SKW_BW_CAP_5G_40M: flags |= IEEE80211_CHAN_NO_HT40; break; case SKW_BW_CAP_5G_80M: flags |= IEEE80211_CHAN_NO_80MHZ; break; case SKW_BW_CAP_5G_160M: flags |= IEEE80211_CHAN_NO_160MHZ; break; default: break; } } } skw_dbg("BW capa: 0x%x, flags: 0x%x\n", chip->fw_bw_capa, flags); #ifdef SKW_SYNC_CHANNEL_FLAGS for (i = 0; i < band->n_channels; i++) band->channels[i].flags = flags; #endif } int skw_setup_wiphy(struct wiphy *wiphy, struct skw_chip_info *chip) { struct skw_core *skw = wiphy_priv(wiphy); wiphy->mgmt_stypes = skw_mgmt_stypes; #if 0 wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; #endif wiphy->flags = WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_REPORTS_OBSS; #ifdef CONFIG_SWT6621S_TDLS wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; #endif #ifdef CONFIG_SWT6621S_OFFCHAN_TX wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX; #else wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) wiphy->max_num_csa_counters = 2; #endif /* STA SME EXTERNAL */ if (!test_bit(SKW_FLAG_STA_SME_EXTERNAL, &skw->flags)) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; /* AP SME INTERNAL */ if (!test_bit(SKW_FLAG_SAP_SME_EXTERNAL, &skw->flags)) { wiphy->max_acl_mac_addrs = SKW_MAX_ACL_ENTRIES; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; wiphy->ap_sme_capa = 1; } wiphy->features = NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | NL80211_FEATURE_VIF_TXPOWER | NL80211_FEATURE_USERSPACE_MPM | NL80211_FEATURE_FULL_AP_CLIENT_STATE | NL80211_FEATURE_INACTIVITY_TIMER; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) //wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; wiphy->features |= NL80211_FEATURE_MAC_ON_CREATE; #endif #ifdef CONFIG_SWT6621S_SCAN_RANDOM_MAC wiphy->features |= SKW_WIPHY_FEATURE_SCAN_RANDOM_MAC; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); //TODO:Add an function to initialize iftype_ext_cap skw_setup_wiphy_iftype_ext_cap(wiphy); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) wiphy->support_mbssid = true; #else wiphy->bss_priv_size = sizeof(struct skw_bss_priv); set_bit(SKW_FLAG_MBSSID_PRIV, &skw->flags); #endif wiphy->interface_modes = BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_MONITOR); #ifndef CONFIG_SWT6621S_LEGACY_P2P wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE); #endif BUILD_BUG_ON_MSG(SKW_EXTENDED_CAPA_LEN > sizeof(skw->ext_capa), "SKW_EXTENDED_CAPA_LEN larger than buffer"); wiphy->extended_capabilities = skw->ext_capa; wiphy->extended_capabilities_mask = skw->ext_capa; wiphy->extended_capabilities_len = SKW_EXTENDED_CAPA_LEN; #if defined(CONFIG_PM) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) wiphy->wowlan = &skw_wowlan_support; #else wiphy->wowlan = skw_wowlan_support; #endif #endif skw_sync_band_capa(&skw_band_2ghz, chip); wiphy->bands[NL80211_BAND_2GHZ] = &skw_band_2ghz; skw_info("2g_only:%d", chip->priv_2g_only); if (!chip->priv_2g_only) { skw_sync_band_capa(&skw_band_5ghz, chip); wiphy->bands[NL80211_BAND_5GHZ] = &skw_band_5ghz; #ifdef CONFIG_SWT6621S_6GHZ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) wiphy->bands[NL80211_BAND_6GHZ] = &skw_band_6ghz; #endif #endif } wiphy->cipher_suites = skw_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(skw_cipher_suites); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->max_scan_ssids = chip->max_scan_ssids; wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; /*2304*/ wiphy->max_remain_on_channel_duration = 500; wiphy->max_sched_scan_ie_len = IEEE80211_MAX_DATA_LEN; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) wiphy->max_sched_scan_reqs = 1; #endif wiphy->max_sched_scan_ssids = 10; wiphy->max_match_sets = 16; /* MCC support */ wiphy->iface_combinations = skw_iface_combos; wiphy->n_iface_combinations = ARRAY_SIZE(skw_iface_combos); wiphy->addresses = skw->address; wiphy->n_addresses = ARRAY_SIZE(skw->address); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) wiphy->max_ap_assoc_sta = skw->fw.max_num_sta; #endif wiphy->reg_notifier = skw_regd_notifier; #ifdef CONFIG_SWT6621S_REGD_SELF_MANAGED #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; #else wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; #endif set_bit(SKW_FLAG_PRIV_REGD, &skw->flags); #endif return wiphy_register(wiphy); }