185630469SLior David // SPDX-License-Identifier: ISC
22be7d22fSVladimir Kondratiev /*
3849a564bSDedy Lansky * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
4bf0353a6SAhmad Masri * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
52be7d22fSVladimir Kondratiev */
62be7d22fSVladimir Kondratiev
7a82553bbSVladimir Kondratiev #include <linux/etherdevice.h>
8949c2d00SJohannes Berg #include <linux/moduleparam.h>
90216a895SLior David #include <net/netlink.h>
104aebd3bdSLior David #include <net/cfg80211.h>
112be7d22fSVladimir Kondratiev #include "wil6210.h"
122be7d22fSVladimir Kondratiev #include "wmi.h"
137bfe9e22SLior David #include "fw.h"
142be7d22fSVladimir Kondratiev
15e6d68341SDedy Lansky #define WIL_MAX_ROC_DURATION_MS 5000
16e6d68341SDedy Lansky
179abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_9_SUBCHANNELS (BIT(0) | BIT(1))
189abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_10_SUBCHANNELS (BIT(1) | BIT(2))
199abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNEL_11_SUBCHANNELS (BIT(2) | BIT(3))
209abe3e30SAlexei Avshalom Lazar
219abe3e30SAlexei Avshalom Lazar /* WIL_EDMG_BW_CONFIGURATION define the allowed channel bandwidth
229abe3e30SAlexei Avshalom Lazar * configurations as defined by IEEE 802.11 section 9.4.2.251, Table 13.
239abe3e30SAlexei Avshalom Lazar * The value 5 allowing CB1 and CB2 of adjacent channels.
249abe3e30SAlexei Avshalom Lazar */
259abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_BW_CONFIGURATION 5
269abe3e30SAlexei Avshalom Lazar
279abe3e30SAlexei Avshalom Lazar /* WIL_EDMG_CHANNELS is a bitmap that indicates the 2.16 GHz channel(s) that
289abe3e30SAlexei Avshalom Lazar * are allowed to be used for EDMG transmissions in the BSS as defined by
299abe3e30SAlexei Avshalom Lazar * IEEE 802.11 section 9.4.2.251.
309abe3e30SAlexei Avshalom Lazar */
319abe3e30SAlexei Avshalom Lazar #define WIL_EDMG_CHANNELS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
329abe3e30SAlexei Avshalom Lazar
33849a564bSDedy Lansky bool disable_ap_sme;
3478484c44SMaya Erez module_param(disable_ap_sme, bool, 0444);
35849a564bSDedy Lansky MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
36849a564bSDedy Lansky
37d1fbf075SMaya Erez #ifdef CONFIG_PM
38d1fbf075SMaya Erez static struct wiphy_wowlan_support wil_wowlan_support = {
39d1fbf075SMaya Erez .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
40d1fbf075SMaya Erez };
41d1fbf075SMaya Erez #endif
42d1fbf075SMaya Erez
432be7d22fSVladimir Kondratiev #define CHAN60G(_channel, _flags) { \
4457fbcce3SJohannes Berg .band = NL80211_BAND_60GHZ, \
452be7d22fSVladimir Kondratiev .center_freq = 56160 + (2160 * (_channel)), \
462be7d22fSVladimir Kondratiev .hw_value = (_channel), \
472be7d22fSVladimir Kondratiev .flags = (_flags), \
482be7d22fSVladimir Kondratiev .max_antenna_gain = 0, \
492be7d22fSVladimir Kondratiev .max_power = 40, \
502be7d22fSVladimir Kondratiev }
512be7d22fSVladimir Kondratiev
522be7d22fSVladimir Kondratiev static struct ieee80211_channel wil_60ghz_channels[] = {
532be7d22fSVladimir Kondratiev CHAN60G(1, 0),
542be7d22fSVladimir Kondratiev CHAN60G(2, 0),
552be7d22fSVladimir Kondratiev CHAN60G(3, 0),
5622b9610eSAlexei Avshalom Lazar CHAN60G(4, 0),
572be7d22fSVladimir Kondratiev };
582be7d22fSVladimir Kondratiev
599abe3e30SAlexei Avshalom Lazar /* Rx channel bonding mode */
609abe3e30SAlexei Avshalom Lazar enum wil_rx_cb_mode {
619abe3e30SAlexei Avshalom Lazar WIL_RX_CB_MODE_DMG,
629abe3e30SAlexei Avshalom Lazar WIL_RX_CB_MODE_EDMG,
639abe3e30SAlexei Avshalom Lazar WIL_RX_CB_MODE_WIDE,
649abe3e30SAlexei Avshalom Lazar };
659abe3e30SAlexei Avshalom Lazar
wil_rx_cb_mode_to_n_bonded(u8 cb_mode)669abe3e30SAlexei Avshalom Lazar static int wil_rx_cb_mode_to_n_bonded(u8 cb_mode)
679abe3e30SAlexei Avshalom Lazar {
689abe3e30SAlexei Avshalom Lazar switch (cb_mode) {
699abe3e30SAlexei Avshalom Lazar case WIL_RX_CB_MODE_DMG:
709abe3e30SAlexei Avshalom Lazar case WIL_RX_CB_MODE_EDMG:
719abe3e30SAlexei Avshalom Lazar return 1;
729abe3e30SAlexei Avshalom Lazar case WIL_RX_CB_MODE_WIDE:
739abe3e30SAlexei Avshalom Lazar return 2;
749abe3e30SAlexei Avshalom Lazar default:
759abe3e30SAlexei Avshalom Lazar return 1;
769abe3e30SAlexei Avshalom Lazar }
779abe3e30SAlexei Avshalom Lazar }
789abe3e30SAlexei Avshalom Lazar
wil_tx_cb_mode_to_n_bonded(u8 cb_mode)799abe3e30SAlexei Avshalom Lazar static int wil_tx_cb_mode_to_n_bonded(u8 cb_mode)
809abe3e30SAlexei Avshalom Lazar {
819abe3e30SAlexei Avshalom Lazar switch (cb_mode) {
829abe3e30SAlexei Avshalom Lazar case WMI_TX_MODE_DMG:
839abe3e30SAlexei Avshalom Lazar case WMI_TX_MODE_EDMG_CB1:
849abe3e30SAlexei Avshalom Lazar return 1;
859abe3e30SAlexei Avshalom Lazar case WMI_TX_MODE_EDMG_CB2:
869abe3e30SAlexei Avshalom Lazar return 2;
879abe3e30SAlexei Avshalom Lazar default:
889abe3e30SAlexei Avshalom Lazar return 1;
899abe3e30SAlexei Avshalom Lazar }
909abe3e30SAlexei Avshalom Lazar }
919abe3e30SAlexei Avshalom Lazar
92e41ab937SDedy Lansky static void
wil_memdup_ie(u8 ** pdst,size_t * pdst_len,const u8 * src,size_t src_len)93e41ab937SDedy Lansky wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
94e41ab937SDedy Lansky {
95e41ab937SDedy Lansky kfree(*pdst);
96e41ab937SDedy Lansky *pdst = NULL;
97e41ab937SDedy Lansky *pdst_len = 0;
98e41ab937SDedy Lansky if (src_len > 0) {
99e41ab937SDedy Lansky *pdst = kmemdup(src, src_len, GFP_KERNEL);
100e41ab937SDedy Lansky if (*pdst)
101e41ab937SDedy Lansky *pdst_len = src_len;
102e41ab937SDedy Lansky }
103e41ab937SDedy Lansky }
104e41ab937SDedy Lansky
wil_num_supported_channels(struct wil6210_priv * wil)10522b9610eSAlexei Avshalom Lazar static int wil_num_supported_channels(struct wil6210_priv *wil)
10622b9610eSAlexei Avshalom Lazar {
10722b9610eSAlexei Avshalom Lazar int num_channels = ARRAY_SIZE(wil_60ghz_channels);
10822b9610eSAlexei Avshalom Lazar
10922b9610eSAlexei Avshalom Lazar if (!test_bit(WMI_FW_CAPABILITY_CHANNEL_4, wil->fw_capabilities))
11022b9610eSAlexei Avshalom Lazar num_channels--;
11122b9610eSAlexei Avshalom Lazar
11222b9610eSAlexei Avshalom Lazar return num_channels;
11322b9610eSAlexei Avshalom Lazar }
11422b9610eSAlexei Avshalom Lazar
update_supported_bands(struct wil6210_priv * wil)11522b9610eSAlexei Avshalom Lazar void update_supported_bands(struct wil6210_priv *wil)
11622b9610eSAlexei Avshalom Lazar {
11722b9610eSAlexei Avshalom Lazar struct wiphy *wiphy = wil_to_wiphy(wil);
11822b9610eSAlexei Avshalom Lazar
11922b9610eSAlexei Avshalom Lazar wil_dbg_misc(wil, "update supported bands");
12022b9610eSAlexei Avshalom Lazar
12122b9610eSAlexei Avshalom Lazar wiphy->bands[NL80211_BAND_60GHZ]->n_channels =
12222b9610eSAlexei Avshalom Lazar wil_num_supported_channels(wil);
1239abe3e30SAlexei Avshalom Lazar
1249abe3e30SAlexei Avshalom Lazar if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING, wil->fw_capabilities)) {
1259abe3e30SAlexei Avshalom Lazar wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.channels =
1269abe3e30SAlexei Avshalom Lazar WIL_EDMG_CHANNELS;
1279abe3e30SAlexei Avshalom Lazar wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.bw_config =
1289abe3e30SAlexei Avshalom Lazar WIL_EDMG_BW_CONFIGURATION;
1299abe3e30SAlexei Avshalom Lazar }
13022b9610eSAlexei Avshalom Lazar }
13122b9610eSAlexei Avshalom Lazar
1320216a895SLior David /* Vendor id to be used in vendor specific command and events
1330216a895SLior David * to user space.
1340216a895SLior David * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
1350216a895SLior David * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and
1360216a895SLior David * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in
1370216a895SLior David * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that
1380216a895SLior David */
1390216a895SLior David
1400216a895SLior David #define QCA_NL80211_VENDOR_ID 0x001374
1410216a895SLior David
1420216a895SLior David #define WIL_MAX_RF_SECTORS (128)
1430216a895SLior David #define WIL_CID_ALL (0xff)
1440216a895SLior David
1450216a895SLior David enum qca_wlan_vendor_attr_rf_sector {
1460216a895SLior David QCA_ATTR_MAC_ADDR = 6,
1470216a895SLior David QCA_ATTR_PAD = 13,
1480216a895SLior David QCA_ATTR_TSF = 29,
1490216a895SLior David QCA_ATTR_DMG_RF_SECTOR_INDEX = 30,
1500216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE = 31,
1510216a895SLior David QCA_ATTR_DMG_RF_MODULE_MASK = 32,
1520216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG = 33,
1530216a895SLior David QCA_ATTR_DMG_RF_SECTOR_MAX,
1540216a895SLior David };
1550216a895SLior David
1560216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_type {
1570216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE_RX,
1580216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE_TX,
1590216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX
1600216a895SLior David };
1610216a895SLior David
1620216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
1630216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
1640216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
1650216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
1660216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
1670216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
1680216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
1690216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
1700216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
1710216a895SLior David
1720216a895SLior David /* keep last */
1730216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
1740216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_MAX =
1750216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
1760216a895SLior David };
1770216a895SLior David
1780216a895SLior David static const struct
1790216a895SLior David nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = {
1800216a895SLior David [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN },
1810216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 },
1820216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 },
1830216a895SLior David [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 },
1840216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED },
1850216a895SLior David };
1860216a895SLior David
1870216a895SLior David static const struct
1880216a895SLior David nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = {
1890216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 },
1900216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 },
1910216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 },
1920216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 },
1930216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 },
1940216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 },
1950216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 },
1960216a895SLior David };
1970216a895SLior David
1980216a895SLior David enum qca_nl80211_vendor_subcmds {
1990216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
2000216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
2010216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
2020216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
2030216a895SLior David };
2040216a895SLior David
2050216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
2060216a895SLior David struct wireless_dev *wdev,
2070216a895SLior David const void *data, int data_len);
2080216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
2090216a895SLior David struct wireless_dev *wdev,
2100216a895SLior David const void *data, int data_len);
2110216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy,
2120216a895SLior David struct wireless_dev *wdev,
2130216a895SLior David const void *data, int data_len);
2140216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy,
2150216a895SLior David struct wireless_dev *wdev,
2160216a895SLior David const void *data, int data_len);
2170216a895SLior David
2180216a895SLior David /* vendor specific commands */
2190216a895SLior David static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = {
2200216a895SLior David {
2210216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID,
2220216a895SLior David .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG,
2230216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2240216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING,
2251667e4f9SJohannes Berg .policy = wil_rf_sector_policy,
2260216a895SLior David .doit = wil_rf_sector_get_cfg
2270216a895SLior David },
2280216a895SLior David {
2290216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID,
2300216a895SLior David .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG,
2310216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2320216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING,
2331667e4f9SJohannes Berg .policy = wil_rf_sector_policy,
2340216a895SLior David .doit = wil_rf_sector_set_cfg
2350216a895SLior David },
2360216a895SLior David {
2370216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID,
2380216a895SLior David .info.subcmd =
2390216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR,
2400216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2410216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING,
2421667e4f9SJohannes Berg .policy = wil_rf_sector_policy,
2430216a895SLior David .doit = wil_rf_sector_get_selected
2440216a895SLior David },
2450216a895SLior David {
2460216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID,
2470216a895SLior David .info.subcmd =
2480216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR,
2490216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
2500216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING,
2511667e4f9SJohannes Berg .policy = wil_rf_sector_policy,
2520216a895SLior David .doit = wil_rf_sector_set_selected
2530216a895SLior David },
2540216a895SLior David };
2550216a895SLior David
2562be7d22fSVladimir Kondratiev static struct ieee80211_supported_band wil_band_60ghz = {
2572be7d22fSVladimir Kondratiev .channels = wil_60ghz_channels,
2582be7d22fSVladimir Kondratiev .n_channels = ARRAY_SIZE(wil_60ghz_channels),
2592be7d22fSVladimir Kondratiev .ht_cap = {
2602be7d22fSVladimir Kondratiev .ht_supported = true,
2612be7d22fSVladimir Kondratiev .cap = 0, /* TODO */
2622be7d22fSVladimir Kondratiev .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
2632be7d22fSVladimir Kondratiev .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
2642be7d22fSVladimir Kondratiev .mcs = {
2652be7d22fSVladimir Kondratiev /* MCS 1..12 - SC PHY */
2662be7d22fSVladimir Kondratiev .rx_mask = {0xfe, 0x1f}, /* 1..12 */
2672be7d22fSVladimir Kondratiev .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
2682be7d22fSVladimir Kondratiev },
2692be7d22fSVladimir Kondratiev },
2702be7d22fSVladimir Kondratiev };
2712be7d22fSVladimir Kondratiev
2722be7d22fSVladimir Kondratiev static const struct ieee80211_txrx_stypes
2732be7d22fSVladimir Kondratiev wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
2742be7d22fSVladimir Kondratiev [NL80211_IFTYPE_STATION] = {
2752be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2762be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2772be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2782be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
2792be7d22fSVladimir Kondratiev },
2802be7d22fSVladimir Kondratiev [NL80211_IFTYPE_AP] = {
2812be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
282849a564bSDedy Lansky BIT(IEEE80211_STYPE_PROBE_RESP >> 4) |
283849a564bSDedy Lansky BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
284b9010f10SAhmad Masri BIT(IEEE80211_STYPE_DISASSOC >> 4) |
285b9010f10SAhmad Masri BIT(IEEE80211_STYPE_AUTH >> 4) |
286b9010f10SAhmad Masri BIT(IEEE80211_STYPE_REASSOC_RESP >> 4),
2872be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
288849a564bSDedy Lansky BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
289849a564bSDedy Lansky BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
290849a564bSDedy Lansky BIT(IEEE80211_STYPE_DISASSOC >> 4) |
291849a564bSDedy Lansky BIT(IEEE80211_STYPE_AUTH >> 4) |
292849a564bSDedy Lansky BIT(IEEE80211_STYPE_DEAUTH >> 4) |
293849a564bSDedy Lansky BIT(IEEE80211_STYPE_REASSOC_REQ >> 4)
2942be7d22fSVladimir Kondratiev },
2952be7d22fSVladimir Kondratiev [NL80211_IFTYPE_P2P_CLIENT] = {
2962be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2972be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
2982be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
2992be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3002be7d22fSVladimir Kondratiev },
3012be7d22fSVladimir Kondratiev [NL80211_IFTYPE_P2P_GO] = {
3022be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3032be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3042be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3052be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3062be7d22fSVladimir Kondratiev },
3074332cac1SLior David [NL80211_IFTYPE_P2P_DEVICE] = {
3084332cac1SLior David .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3094332cac1SLior David BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
3104332cac1SLior David .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
3114332cac1SLior David BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
3124332cac1SLior David },
3132be7d22fSVladimir Kondratiev };
3142be7d22fSVladimir Kondratiev
3152be7d22fSVladimir Kondratiev static const u32 wil_cipher_suites[] = {
3162be7d22fSVladimir Kondratiev WLAN_CIPHER_SUITE_GCMP,
3172be7d22fSVladimir Kondratiev };
3182be7d22fSVladimir Kondratiev
31958527421SVladimir Kondratiev static const char * const key_usage_str[] = {
32058527421SVladimir Kondratiev [WMI_KEY_USE_PAIRWISE] = "PTK",
32158527421SVladimir Kondratiev [WMI_KEY_USE_RX_GROUP] = "RX_GTK",
32258527421SVladimir Kondratiev [WMI_KEY_USE_TX_GROUP] = "TX_GTK",
32342fe1e51SAhmad Masri [WMI_KEY_USE_STORE_PTK] = "STORE_PTK",
32442fe1e51SAhmad Masri [WMI_KEY_USE_APPLY_PTK] = "APPLY_PTK",
32558527421SVladimir Kondratiev };
32658527421SVladimir Kondratiev
wil_iftype_nl2wmi(enum nl80211_iftype type)3272be7d22fSVladimir Kondratiev int wil_iftype_nl2wmi(enum nl80211_iftype type)
3282be7d22fSVladimir Kondratiev {
3292be7d22fSVladimir Kondratiev static const struct {
3302be7d22fSVladimir Kondratiev enum nl80211_iftype nl;
3312be7d22fSVladimir Kondratiev enum wmi_network_type wmi;
3322be7d22fSVladimir Kondratiev } __nl2wmi[] = {
3332be7d22fSVladimir Kondratiev {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC},
3342be7d22fSVladimir Kondratiev {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA},
3352be7d22fSVladimir Kondratiev {NL80211_IFTYPE_AP, WMI_NETTYPE_AP},
3362be7d22fSVladimir Kondratiev {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P},
3372be7d22fSVladimir Kondratiev {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P},
3382be7d22fSVladimir Kondratiev {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */
3392be7d22fSVladimir Kondratiev };
3402be7d22fSVladimir Kondratiev uint i;
3412be7d22fSVladimir Kondratiev
3422be7d22fSVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
3432be7d22fSVladimir Kondratiev if (__nl2wmi[i].nl == type)
3442be7d22fSVladimir Kondratiev return __nl2wmi[i].wmi;
3452be7d22fSVladimir Kondratiev }
3462be7d22fSVladimir Kondratiev
3472be7d22fSVladimir Kondratiev return -EOPNOTSUPP;
3482be7d22fSVladimir Kondratiev }
3492be7d22fSVladimir Kondratiev
wil_spec2wmi_ch(u8 spec_ch,u8 * wmi_ch)3509abe3e30SAlexei Avshalom Lazar int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch)
3519abe3e30SAlexei Avshalom Lazar {
3529abe3e30SAlexei Avshalom Lazar switch (spec_ch) {
3539abe3e30SAlexei Avshalom Lazar case 1:
3549abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_1;
3559abe3e30SAlexei Avshalom Lazar break;
3569abe3e30SAlexei Avshalom Lazar case 2:
3579abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_2;
3589abe3e30SAlexei Avshalom Lazar break;
3599abe3e30SAlexei Avshalom Lazar case 3:
3609abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_3;
3619abe3e30SAlexei Avshalom Lazar break;
3629abe3e30SAlexei Avshalom Lazar case 4:
3639abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_4;
3649abe3e30SAlexei Avshalom Lazar break;
3659abe3e30SAlexei Avshalom Lazar case 5:
3669abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_5;
3679abe3e30SAlexei Avshalom Lazar break;
3689abe3e30SAlexei Avshalom Lazar case 6:
3699abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_6;
3709abe3e30SAlexei Avshalom Lazar break;
3719abe3e30SAlexei Avshalom Lazar case 9:
3729abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_9;
3739abe3e30SAlexei Avshalom Lazar break;
3749abe3e30SAlexei Avshalom Lazar case 10:
3759abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_10;
3769abe3e30SAlexei Avshalom Lazar break;
3779abe3e30SAlexei Avshalom Lazar case 11:
3789abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_11;
3799abe3e30SAlexei Avshalom Lazar break;
3809abe3e30SAlexei Avshalom Lazar case 12:
3819abe3e30SAlexei Avshalom Lazar *wmi_ch = WMI_CHANNEL_12;
3829abe3e30SAlexei Avshalom Lazar break;
3839abe3e30SAlexei Avshalom Lazar default:
3849abe3e30SAlexei Avshalom Lazar return -EINVAL;
3859abe3e30SAlexei Avshalom Lazar }
3869abe3e30SAlexei Avshalom Lazar
3879abe3e30SAlexei Avshalom Lazar return 0;
3889abe3e30SAlexei Avshalom Lazar }
3899abe3e30SAlexei Avshalom Lazar
wil_wmi2spec_ch(u8 wmi_ch,u8 * spec_ch)3909abe3e30SAlexei Avshalom Lazar int wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch)
3919abe3e30SAlexei Avshalom Lazar {
3929abe3e30SAlexei Avshalom Lazar switch (wmi_ch) {
3939abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_1:
3949abe3e30SAlexei Avshalom Lazar *spec_ch = 1;
3959abe3e30SAlexei Avshalom Lazar break;
3969abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_2:
3979abe3e30SAlexei Avshalom Lazar *spec_ch = 2;
3989abe3e30SAlexei Avshalom Lazar break;
3999abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_3:
4009abe3e30SAlexei Avshalom Lazar *spec_ch = 3;
4019abe3e30SAlexei Avshalom Lazar break;
4029abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_4:
4039abe3e30SAlexei Avshalom Lazar *spec_ch = 4;
4049abe3e30SAlexei Avshalom Lazar break;
4059abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_5:
4069abe3e30SAlexei Avshalom Lazar *spec_ch = 5;
4079abe3e30SAlexei Avshalom Lazar break;
4089abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_6:
4099abe3e30SAlexei Avshalom Lazar *spec_ch = 6;
4109abe3e30SAlexei Avshalom Lazar break;
4119abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_9:
4129abe3e30SAlexei Avshalom Lazar *spec_ch = 9;
4139abe3e30SAlexei Avshalom Lazar break;
4149abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_10:
4159abe3e30SAlexei Avshalom Lazar *spec_ch = 10;
4169abe3e30SAlexei Avshalom Lazar break;
4179abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_11:
4189abe3e30SAlexei Avshalom Lazar *spec_ch = 11;
4199abe3e30SAlexei Avshalom Lazar break;
4209abe3e30SAlexei Avshalom Lazar case WMI_CHANNEL_12:
4219abe3e30SAlexei Avshalom Lazar *spec_ch = 12;
4229abe3e30SAlexei Avshalom Lazar break;
4239abe3e30SAlexei Avshalom Lazar default:
4249abe3e30SAlexei Avshalom Lazar return -EINVAL;
4259abe3e30SAlexei Avshalom Lazar }
4269abe3e30SAlexei Avshalom Lazar
4279abe3e30SAlexei Avshalom Lazar return 0;
4289abe3e30SAlexei Avshalom Lazar }
4299abe3e30SAlexei Avshalom Lazar
wil_cid_fill_sinfo(struct wil6210_vif * vif,int cid,struct station_info * sinfo)430e00243faSLior David int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
431ef28afdbSVladimir Kondratiev struct station_info *sinfo)
4322be7d22fSVladimir Kondratiev {
433e00243faSLior David struct wil6210_priv *wil = vif_to_wil(vif);
4342be7d22fSVladimir Kondratiev struct wmi_notify_req_cmd cmd = {
4353df2cd36SVladimir Kondratiev .cid = cid,
4362be7d22fSVladimir Kondratiev .interval_usec = 0,
4372be7d22fSVladimir Kondratiev };
438ef28afdbSVladimir Kondratiev struct {
439b874ddecSLior David struct wmi_cmd_hdr wmi;
440ef28afdbSVladimir Kondratiev struct wmi_notify_req_done_event evt;
441ef28afdbSVladimir Kondratiev } __packed reply;
442c8b78b5fSVladimir Kondratiev struct wil_net_stats *stats = &wil->sta[cid].stats;
443ef28afdbSVladimir Kondratiev int rc;
4447064e219SMax Chen u8 tx_mcs, rx_mcs;
4457064e219SMax Chen u8 tx_rate_flag = RATE_INFO_FLAGS_DMG;
4467064e219SMax Chen u8 rx_rate_flag = RATE_INFO_FLAGS_DMG;
4472be7d22fSVladimir Kondratiev
448807b0860SAlexei Avshalom Lazar memset(&reply, 0, sizeof(reply));
449807b0860SAlexei Avshalom Lazar
450e00243faSLior David rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd),
4512a32c20bSAhmad Masri WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply),
4522a32c20bSAhmad Masri WIL_WMI_CALL_GENERAL_TO_MS);
4532be7d22fSVladimir Kondratiev if (rc)
4542be7d22fSVladimir Kondratiev return rc;
4552be7d22fSVladimir Kondratiev
4567064e219SMax Chen tx_mcs = le16_to_cpu(reply.evt.bf_mcs);
4577064e219SMax Chen
458e00243faSLior David wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n"
4597064e219SMax Chen " MCS %s TSF 0x%016llx\n"
46030868f5dSDedy Lansky " BF status 0x%08x RSSI %d SQI %d%%\n"
461c8b78b5fSVladimir Kondratiev " Tx Tpt %d goodput %d Rx goodput %d\n"
4629abe3e30SAlexei Avshalom Lazar " Sectors(rx:tx) my %d:%d peer %d:%d\n"
4639abe3e30SAlexei Avshalom Lazar " Tx mode %d}\n",
4647064e219SMax Chen cid, vif->mid, WIL_EXTENDED_MCS_CHECK(tx_mcs),
465c8b78b5fSVladimir Kondratiev le64_to_cpu(reply.evt.tsf), reply.evt.status,
46630868f5dSDedy Lansky reply.evt.rssi,
467b8b33a3aSVladimir Kondratiev reply.evt.sqi,
468c8b78b5fSVladimir Kondratiev le32_to_cpu(reply.evt.tx_tpt),
469c8b78b5fSVladimir Kondratiev le32_to_cpu(reply.evt.tx_goodput),
470c8b78b5fSVladimir Kondratiev le32_to_cpu(reply.evt.rx_goodput),
471c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.my_rx_sector),
472c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.my_tx_sector),
473c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.other_rx_sector),
4749abe3e30SAlexei Avshalom Lazar le16_to_cpu(reply.evt.other_tx_sector),
4759abe3e30SAlexei Avshalom Lazar reply.evt.tx_mode);
476c8b78b5fSVladimir Kondratiev
4772be7d22fSVladimir Kondratiev sinfo->generation = wil->sinfo_gen;
4782be7d22fSVladimir Kondratiev
47922d0d2faSOmer Efrat sinfo->filled = BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
48022d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
48122d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
48222d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
48322d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_RX_BITRATE) |
48422d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_TX_BITRATE) |
48522d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
48622d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_TX_FAILED);
487c8b78b5fSVladimir Kondratiev
4887064e219SMax Chen if (wil->use_enhanced_dma_hw && reply.evt.tx_mode != WMI_TX_MODE_DMG) {
4897064e219SMax Chen tx_rate_flag = RATE_INFO_FLAGS_EDMG;
4907064e219SMax Chen rx_rate_flag = RATE_INFO_FLAGS_EDMG;
4917064e219SMax Chen }
4929abe3e30SAlexei Avshalom Lazar
4937064e219SMax Chen rx_mcs = stats->last_mcs_rx;
4947064e219SMax Chen
4957064e219SMax Chen /* check extended MCS (12.1) and convert it into
4967064e219SMax Chen * base MCS (7) + EXTENDED_SC_DMG flag
4977064e219SMax Chen */
4987064e219SMax Chen if (tx_mcs == WIL_EXTENDED_MCS_26) {
4997064e219SMax Chen tx_rate_flag = RATE_INFO_FLAGS_EXTENDED_SC_DMG;
5007064e219SMax Chen tx_mcs = WIL_BASE_MCS_FOR_EXTENDED_26;
5017064e219SMax Chen }
5027064e219SMax Chen if (rx_mcs == WIL_EXTENDED_MCS_26) {
5037064e219SMax Chen rx_rate_flag = RATE_INFO_FLAGS_EXTENDED_SC_DMG;
5047064e219SMax Chen rx_mcs = WIL_BASE_MCS_FOR_EXTENDED_26;
5057064e219SMax Chen }
5067064e219SMax Chen
5077064e219SMax Chen sinfo->txrate.flags = tx_rate_flag;
5087064e219SMax Chen sinfo->rxrate.flags = rx_rate_flag;
5097064e219SMax Chen sinfo->txrate.mcs = tx_mcs;
5107064e219SMax Chen sinfo->rxrate.mcs = rx_mcs;
5117064e219SMax Chen
5129abe3e30SAlexei Avshalom Lazar sinfo->txrate.n_bonded_ch =
5139abe3e30SAlexei Avshalom Lazar wil_tx_cb_mode_to_n_bonded(reply.evt.tx_mode);
5149abe3e30SAlexei Avshalom Lazar sinfo->rxrate.n_bonded_ch =
5159abe3e30SAlexei Avshalom Lazar wil_rx_cb_mode_to_n_bonded(stats->last_cb_mode_rx);
516c8b78b5fSVladimir Kondratiev sinfo->rx_bytes = stats->rx_bytes;
517c8b78b5fSVladimir Kondratiev sinfo->rx_packets = stats->rx_packets;
518c8b78b5fSVladimir Kondratiev sinfo->rx_dropped_misc = stats->rx_dropped;
519c8b78b5fSVladimir Kondratiev sinfo->tx_bytes = stats->tx_bytes;
520c8b78b5fSVladimir Kondratiev sinfo->tx_packets = stats->tx_packets;
521c8b78b5fSVladimir Kondratiev sinfo->tx_failed = stats->tx_errors;
5222be7d22fSVladimir Kondratiev
5235bd60982SLior David if (test_bit(wil_vif_fwconnected, vif->status)) {
52422d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
52530868f5dSDedy Lansky if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
52630868f5dSDedy Lansky wil->fw_capabilities))
52730868f5dSDedy Lansky sinfo->signal = reply.evt.rssi;
52830868f5dSDedy Lansky else
529b8b33a3aSVladimir Kondratiev sinfo->signal = reply.evt.sqi;
5302be7d22fSVladimir Kondratiev }
5312be7d22fSVladimir Kondratiev
532ef28afdbSVladimir Kondratiev return rc;
533ef28afdbSVladimir Kondratiev }
534ef28afdbSVladimir Kondratiev
wil_cfg80211_get_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_info * sinfo)535ef28afdbSVladimir Kondratiev static int wil_cfg80211_get_station(struct wiphy *wiphy,
536ef28afdbSVladimir Kondratiev struct net_device *ndev,
5373b3a0162SJohannes Berg const u8 *mac, struct station_info *sinfo)
538ef28afdbSVladimir Kondratiev {
539e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
540ef28afdbSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
541ef28afdbSVladimir Kondratiev int rc;
542ef28afdbSVladimir Kondratiev
543e00243faSLior David int cid = wil_find_cid(wil, vif->mid, mac);
544ef28afdbSVladimir Kondratiev
545e00243faSLior David wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid,
546e00243faSLior David vif->mid);
547c478ac9dSAlexei Avshalom Lazar if (!wil_cid_valid(wil, cid))
548c478ac9dSAlexei Avshalom Lazar return -ENOENT;
549ef28afdbSVladimir Kondratiev
550e00243faSLior David rc = wil_cid_fill_sinfo(vif, cid, sinfo);
551ef28afdbSVladimir Kondratiev
552ef28afdbSVladimir Kondratiev return rc;
553ef28afdbSVladimir Kondratiev }
554ef28afdbSVladimir Kondratiev
555ef28afdbSVladimir Kondratiev /*
556e00243faSLior David * Find @idx-th active STA for specific MID for station dump.
557ef28afdbSVladimir Kondratiev */
wil_find_cid_by_idx(struct wil6210_priv * wil,u8 mid,int idx)55842fe1e51SAhmad Masri int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
559ef28afdbSVladimir Kondratiev {
560ef28afdbSVladimir Kondratiev int i;
561ef28afdbSVladimir Kondratiev
562ddf7afddSAhmad Masri for (i = 0; i < wil->max_assoc_sta; i++) {
563ef28afdbSVladimir Kondratiev if (wil->sta[i].status == wil_sta_unused)
564ef28afdbSVladimir Kondratiev continue;
565e00243faSLior David if (wil->sta[i].mid != mid)
566e00243faSLior David continue;
567ef28afdbSVladimir Kondratiev if (idx == 0)
568ef28afdbSVladimir Kondratiev return i;
569ef28afdbSVladimir Kondratiev idx--;
570ef28afdbSVladimir Kondratiev }
571ef28afdbSVladimir Kondratiev
572ef28afdbSVladimir Kondratiev return -ENOENT;
573ef28afdbSVladimir Kondratiev }
574ef28afdbSVladimir Kondratiev
wil_cfg80211_dump_station(struct wiphy * wiphy,struct net_device * dev,int idx,u8 * mac,struct station_info * sinfo)575ef28afdbSVladimir Kondratiev static int wil_cfg80211_dump_station(struct wiphy *wiphy,
576ef28afdbSVladimir Kondratiev struct net_device *dev, int idx,
577ef28afdbSVladimir Kondratiev u8 *mac, struct station_info *sinfo)
578ef28afdbSVladimir Kondratiev {
579e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
580ef28afdbSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
581ef28afdbSVladimir Kondratiev int rc;
582e00243faSLior David int cid = wil_find_cid_by_idx(wil, vif->mid, idx);
583ef28afdbSVladimir Kondratiev
584c478ac9dSAlexei Avshalom Lazar if (!wil_cid_valid(wil, cid))
585ef28afdbSVladimir Kondratiev return -ENOENT;
586ef28afdbSVladimir Kondratiev
587a82553bbSVladimir Kondratiev ether_addr_copy(mac, wil->sta[cid].addr);
588e00243faSLior David wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid,
589e00243faSLior David vif->mid);
590ef28afdbSVladimir Kondratiev
591e00243faSLior David rc = wil_cid_fill_sinfo(vif, cid, sinfo);
592ef28afdbSVladimir Kondratiev
593ef28afdbSVladimir Kondratiev return rc;
5942be7d22fSVladimir Kondratiev }
5952be7d22fSVladimir Kondratiev
wil_cfg80211_start_p2p_device(struct wiphy * wiphy,struct wireless_dev * wdev)59669fecf59SLior David static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
59769fecf59SLior David struct wireless_dev *wdev)
59869fecf59SLior David {
59969fecf59SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy);
60069fecf59SLior David
60169fecf59SLior David wil_dbg_misc(wil, "start_p2p_device: entered\n");
602e00243faSLior David wil->p2p_dev_started = 1;
60369fecf59SLior David return 0;
60469fecf59SLior David }
60569fecf59SLior David
wil_cfg80211_stop_p2p_device(struct wiphy * wiphy,struct wireless_dev * wdev)60669fecf59SLior David static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
60769fecf59SLior David struct wireless_dev *wdev)
60869fecf59SLior David {
60969fecf59SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy);
61069fecf59SLior David
611e00243faSLior David if (!wil->p2p_dev_started)
61269fecf59SLior David return;
61369fecf59SLior David
61469fecf59SLior David wil_dbg_misc(wil, "stop_p2p_device: entered\n");
61569fecf59SLior David mutex_lock(&wil->mutex);
616404bbb3cSLior David mutex_lock(&wil->vif_mutex);
61769fecf59SLior David wil_p2p_stop_radio_operations(wil);
618e00243faSLior David wil->p2p_dev_started = 0;
619404bbb3cSLior David mutex_unlock(&wil->vif_mutex);
62069fecf59SLior David mutex_unlock(&wil->mutex);
62169fecf59SLior David }
62269fecf59SLior David
wil_cfg80211_validate_add_iface(struct wil6210_priv * wil,enum nl80211_iftype new_type)6234aebd3bdSLior David static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil,
6244aebd3bdSLior David enum nl80211_iftype new_type)
6254aebd3bdSLior David {
6264aebd3bdSLior David int i;
6274aebd3bdSLior David struct wireless_dev *wdev;
6284aebd3bdSLior David struct iface_combination_params params = {
6294aebd3bdSLior David .num_different_channels = 1,
6304aebd3bdSLior David };
6314aebd3bdSLior David
632e4a29bddSAhmad Masri for (i = 0; i < GET_MAX_VIFS(wil); i++) {
6334aebd3bdSLior David if (wil->vifs[i]) {
6344aebd3bdSLior David wdev = vif_to_wdev(wil->vifs[i]);
6354aebd3bdSLior David params.iftype_num[wdev->iftype]++;
6364aebd3bdSLior David }
6374aebd3bdSLior David }
6384aebd3bdSLior David params.iftype_num[new_type]++;
6394aebd3bdSLior David return cfg80211_check_combinations(wil->wiphy, ¶ms);
6404aebd3bdSLior David }
6414aebd3bdSLior David
wil_cfg80211_validate_change_iface(struct wil6210_priv * wil,struct wil6210_vif * vif,enum nl80211_iftype new_type)6424aebd3bdSLior David static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil,
6434aebd3bdSLior David struct wil6210_vif *vif,
6444aebd3bdSLior David enum nl80211_iftype new_type)
6454aebd3bdSLior David {
6464aebd3bdSLior David int i, ret = 0;
6474aebd3bdSLior David struct wireless_dev *wdev;
6484aebd3bdSLior David struct iface_combination_params params = {
6494aebd3bdSLior David .num_different_channels = 1,
6504aebd3bdSLior David };
6514aebd3bdSLior David bool check_combos = false;
6524aebd3bdSLior David
653e4a29bddSAhmad Masri for (i = 0; i < GET_MAX_VIFS(wil); i++) {
6544aebd3bdSLior David struct wil6210_vif *vif_pos = wil->vifs[i];
6554aebd3bdSLior David
6564aebd3bdSLior David if (vif_pos && vif != vif_pos) {
6574aebd3bdSLior David wdev = vif_to_wdev(vif_pos);
6584aebd3bdSLior David params.iftype_num[wdev->iftype]++;
6594aebd3bdSLior David check_combos = true;
6604aebd3bdSLior David }
6614aebd3bdSLior David }
6624aebd3bdSLior David
6634aebd3bdSLior David if (check_combos) {
6644aebd3bdSLior David params.iftype_num[new_type]++;
6654aebd3bdSLior David ret = cfg80211_check_combinations(wil->wiphy, ¶ms);
6664aebd3bdSLior David }
6674aebd3bdSLior David return ret;
6684aebd3bdSLior David }
6694aebd3bdSLior David
6704332cac1SLior David static struct wireless_dev *
wil_cfg80211_add_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)6714332cac1SLior David wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
6724332cac1SLior David unsigned char name_assign_type,
6734332cac1SLior David enum nl80211_iftype type,
674818a986eSJohannes Berg struct vif_params *params)
6754332cac1SLior David {
6764332cac1SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy);
6774aebd3bdSLior David struct net_device *ndev_main = wil->main_ndev, *ndev;
6789f38f286SLior David struct wil6210_vif *vif;
6794aebd3bdSLior David struct wireless_dev *p2p_wdev, *wdev;
6804aebd3bdSLior David int rc;
6814332cac1SLior David
6824aebd3bdSLior David wil_dbg_misc(wil, "add_iface, type %d\n", type);
6834332cac1SLior David
6844aebd3bdSLior David /* P2P device is not a real virtual interface, it is a management-only
6854aebd3bdSLior David * interface that shares the main interface.
6864aebd3bdSLior David * Skip concurrency checks here.
6874aebd3bdSLior David */
6884aebd3bdSLior David if (type == NL80211_IFTYPE_P2P_DEVICE) {
6894332cac1SLior David if (wil->p2p_wdev) {
690af3db60aSLazar Alexei wil_err(wil, "P2P_DEVICE interface already created\n");
6914332cac1SLior David return ERR_PTR(-EINVAL);
6924332cac1SLior David }
6934332cac1SLior David
6945bd60982SLior David p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
6955bd60982SLior David if (!p2p_wdev)
6964332cac1SLior David return ERR_PTR(-ENOMEM);
6974332cac1SLior David
6984332cac1SLior David p2p_wdev->iftype = type;
6994332cac1SLior David p2p_wdev->wiphy = wiphy;
7004332cac1SLior David /* use our primary ethernet address */
7014aebd3bdSLior David ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr);
7024332cac1SLior David
7034332cac1SLior David wil->p2p_wdev = p2p_wdev;
7044332cac1SLior David
7054332cac1SLior David return p2p_wdev;
7064332cac1SLior David }
7074332cac1SLior David
7084aebd3bdSLior David if (!wil->wiphy->n_iface_combinations) {
7094aebd3bdSLior David wil_err(wil, "virtual interfaces not supported\n");
7104aebd3bdSLior David return ERR_PTR(-EINVAL);
7114aebd3bdSLior David }
7124aebd3bdSLior David
7134aebd3bdSLior David rc = wil_cfg80211_validate_add_iface(wil, type);
7144aebd3bdSLior David if (rc) {
7154aebd3bdSLior David wil_err(wil, "iface validation failed, err=%d\n", rc);
7164aebd3bdSLior David return ERR_PTR(rc);
7174aebd3bdSLior David }
7184aebd3bdSLior David
7194aebd3bdSLior David vif = wil_vif_alloc(wil, name, name_assign_type, type);
7204aebd3bdSLior David if (IS_ERR(vif))
7214aebd3bdSLior David return ERR_CAST(vif);
7224aebd3bdSLior David
7234aebd3bdSLior David ndev = vif_to_ndev(vif);
7244aebd3bdSLior David ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr);
7254aebd3bdSLior David if (is_valid_ether_addr(params->macaddr)) {
726fcb79f31SJakub Kicinski eth_hw_addr_set(ndev, params->macaddr);
7274aebd3bdSLior David } else {
728c7b6128aSJakub Kicinski u8 addr[ETH_ALEN];
729c7b6128aSJakub Kicinski
730c7b6128aSJakub Kicinski ether_addr_copy(addr, ndev_main->perm_addr);
731c7b6128aSJakub Kicinski addr[0] = (addr[0] ^ (1 << vif->mid)) | 0x2; /* locally administered */
732c7b6128aSJakub Kicinski eth_hw_addr_set(ndev, addr);
7334aebd3bdSLior David }
7344aebd3bdSLior David wdev = vif_to_wdev(vif);
7354aebd3bdSLior David ether_addr_copy(wdev->address, ndev->dev_addr);
7364aebd3bdSLior David
7374aebd3bdSLior David rc = wil_vif_add(wil, vif);
7384aebd3bdSLior David if (rc)
7394aebd3bdSLior David goto out;
7404aebd3bdSLior David
7414aebd3bdSLior David wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n",
7424aebd3bdSLior David vif->mid, type, wdev->address);
7434aebd3bdSLior David return wdev;
7444aebd3bdSLior David out:
7454aebd3bdSLior David wil_vif_free(vif);
7464aebd3bdSLior David return ERR_PTR(rc);
7474aebd3bdSLior David }
7484aebd3bdSLior David
wil_vif_prepare_stop(struct wil6210_vif * vif)7493ada9314SLior David int wil_vif_prepare_stop(struct wil6210_vif *vif)
7504aebd3bdSLior David {
7513ada9314SLior David struct wil6210_priv *wil = vif_to_wil(vif);
7524aebd3bdSLior David struct wireless_dev *wdev = vif_to_wdev(vif);
7534aebd3bdSLior David struct net_device *ndev;
7544aebd3bdSLior David int rc;
7554aebd3bdSLior David
7564aebd3bdSLior David if (wdev->iftype != NL80211_IFTYPE_AP)
7574aebd3bdSLior David return 0;
7584aebd3bdSLior David
7594aebd3bdSLior David ndev = vif_to_ndev(vif);
7604aebd3bdSLior David if (netif_carrier_ok(ndev)) {
7614aebd3bdSLior David rc = wmi_pcp_stop(vif);
7624aebd3bdSLior David if (rc) {
7634aebd3bdSLior David wil_info(wil, "failed to stop AP, status %d\n",
7644aebd3bdSLior David rc);
7654aebd3bdSLior David /* continue */
7664aebd3bdSLior David }
7673ada9314SLior David wil_bcast_fini(vif);
7684aebd3bdSLior David netif_carrier_off(ndev);
7694aebd3bdSLior David }
7704aebd3bdSLior David
7714aebd3bdSLior David return 0;
7724aebd3bdSLior David }
7734aebd3bdSLior David
wil_cfg80211_del_iface(struct wiphy * wiphy,struct wireless_dev * wdev)7744332cac1SLior David static int wil_cfg80211_del_iface(struct wiphy *wiphy,
7754332cac1SLior David struct wireless_dev *wdev)
7764332cac1SLior David {
7774332cac1SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy);
7784aebd3bdSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
7794aebd3bdSLior David int rc;
7804332cac1SLior David
781af3db60aSLazar Alexei wil_dbg_misc(wil, "del_iface\n");
7824332cac1SLior David
7834aebd3bdSLior David if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
7844332cac1SLior David if (wdev != wil->p2p_wdev) {
7854aebd3bdSLior David wil_err(wil, "delete of incorrect interface 0x%p\n",
7864aebd3bdSLior David wdev);
7874332cac1SLior David return -EINVAL;
7884332cac1SLior David }
7894332cac1SLior David
79069fecf59SLior David wil_cfg80211_stop_p2p_device(wiphy, wdev);
7914332cac1SLior David wil_p2p_wdev_free(wil);
7924332cac1SLior David return 0;
7934332cac1SLior David }
7944332cac1SLior David
7954aebd3bdSLior David if (vif->mid == 0) {
7964aebd3bdSLior David wil_err(wil, "cannot remove the main interface\n");
7974aebd3bdSLior David return -EINVAL;
7984aebd3bdSLior David }
7994aebd3bdSLior David
8003ada9314SLior David rc = wil_vif_prepare_stop(vif);
8014aebd3bdSLior David if (rc)
8024aebd3bdSLior David goto out;
8034aebd3bdSLior David
8044aebd3bdSLior David wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
8054aebd3bdSLior David vif->mid, wdev->iftype, wdev->address);
8064aebd3bdSLior David
8074aebd3bdSLior David wil_vif_remove(wil, vif->mid);
8084aebd3bdSLior David out:
8094aebd3bdSLior David return rc;
8104aebd3bdSLior David }
8114aebd3bdSLior David
wil_is_safe_switch(enum nl80211_iftype from,enum nl80211_iftype to)812b913e330SAlexei Avshalom Lazar static bool wil_is_safe_switch(enum nl80211_iftype from,
813b913e330SAlexei Avshalom Lazar enum nl80211_iftype to)
814b913e330SAlexei Avshalom Lazar {
815b913e330SAlexei Avshalom Lazar if (from == NL80211_IFTYPE_STATION &&
816b913e330SAlexei Avshalom Lazar to == NL80211_IFTYPE_P2P_CLIENT)
817b913e330SAlexei Avshalom Lazar return true;
818b913e330SAlexei Avshalom Lazar
819b913e330SAlexei Avshalom Lazar return false;
820b913e330SAlexei Avshalom Lazar }
821b913e330SAlexei Avshalom Lazar
wil_cfg80211_change_iface(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type,struct vif_params * params)8222be7d22fSVladimir Kondratiev static int wil_cfg80211_change_iface(struct wiphy *wiphy,
8232be7d22fSVladimir Kondratiev struct net_device *ndev,
824818a986eSJohannes Berg enum nl80211_iftype type,
8252be7d22fSVladimir Kondratiev struct vif_params *params)
8262be7d22fSVladimir Kondratiev {
8272be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
828e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
829e00243faSLior David struct wireless_dev *wdev = vif_to_wdev(vif);
830e6d68341SDedy Lansky int rc;
8313ada9314SLior David bool fw_reset = false;
832e6d68341SDedy Lansky
833af3db60aSLazar Alexei wil_dbg_misc(wil, "change_iface: type=%d\n", type);
834e6d68341SDedy Lansky
8354aebd3bdSLior David if (wiphy->n_iface_combinations) {
8364aebd3bdSLior David rc = wil_cfg80211_validate_change_iface(wil, vif, type);
8374aebd3bdSLior David if (rc) {
8384aebd3bdSLior David wil_err(wil, "iface validation failed, err=%d\n", rc);
8394aebd3bdSLior David return rc;
8404aebd3bdSLior David }
8414aebd3bdSLior David }
8424aebd3bdSLior David
8434aebd3bdSLior David /* do not reset FW when there are active VIFs,
8444aebd3bdSLior David * because it can cause significant disruption
8454aebd3bdSLior David */
8463ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, true, false) &&
847b913e330SAlexei Avshalom Lazar netif_running(ndev) && !wil_is_recovery_blocked(wil) &&
848b913e330SAlexei Avshalom Lazar !wil_is_safe_switch(wdev->iftype, type)) {
849e6d68341SDedy Lansky wil_dbg_misc(wil, "interface is up. resetting...\n");
850e6d68341SDedy Lansky mutex_lock(&wil->mutex);
851e6d68341SDedy Lansky __wil_down(wil);
852e6d68341SDedy Lansky rc = __wil_up(wil);
853e6d68341SDedy Lansky mutex_unlock(&wil->mutex);
854e6d68341SDedy Lansky
855e6d68341SDedy Lansky if (rc)
856e6d68341SDedy Lansky return rc;
8573ada9314SLior David fw_reset = true;
858e6d68341SDedy Lansky }
8592be7d22fSVladimir Kondratiev
8602be7d22fSVladimir Kondratiev switch (type) {
8612be7d22fSVladimir Kondratiev case NL80211_IFTYPE_STATION:
8622be7d22fSVladimir Kondratiev case NL80211_IFTYPE_AP:
8632be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT:
8642be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_GO:
8652be7d22fSVladimir Kondratiev break;
8662be7d22fSVladimir Kondratiev case NL80211_IFTYPE_MONITOR:
867818a986eSJohannes Berg if (params->flags)
868818a986eSJohannes Berg wil->monitor_flags = params->flags;
8692be7d22fSVladimir Kondratiev break;
8702be7d22fSVladimir Kondratiev default:
8712be7d22fSVladimir Kondratiev return -EOPNOTSUPP;
8722be7d22fSVladimir Kondratiev }
8732be7d22fSVladimir Kondratiev
8743ada9314SLior David if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
8753ada9314SLior David if (!fw_reset)
8763ada9314SLior David wil_vif_prepare_stop(vif);
8774aebd3bdSLior David rc = wmi_port_delete(wil, vif->mid);
8784aebd3bdSLior David if (rc)
8794aebd3bdSLior David return rc;
8804aebd3bdSLior David rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
8814aebd3bdSLior David if (rc)
8824aebd3bdSLior David return rc;
8834aebd3bdSLior David }
8842be7d22fSVladimir Kondratiev
8854aebd3bdSLior David wdev->iftype = type;
8862be7d22fSVladimir Kondratiev return 0;
8872be7d22fSVladimir Kondratiev }
8882be7d22fSVladimir Kondratiev
wil_cfg80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * request)8892be7d22fSVladimir Kondratiev static int wil_cfg80211_scan(struct wiphy *wiphy,
8902be7d22fSVladimir Kondratiev struct cfg80211_scan_request *request)
8912be7d22fSVladimir Kondratiev {
8922be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
8934332cac1SLior David struct wireless_dev *wdev = request->wdev;
894e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
8952be7d22fSVladimir Kondratiev struct {
8962be7d22fSVladimir Kondratiev struct wmi_start_scan_cmd cmd;
8972be7d22fSVladimir Kondratiev u16 chnl[4];
8982be7d22fSVladimir Kondratiev } __packed cmd;
8992be7d22fSVladimir Kondratiev uint i, n;
900ed6f9dc6SVladimir Kondratiev int rc;
9012be7d22fSVladimir Kondratiev
902af3db60aSLazar Alexei wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
903e6d68341SDedy Lansky
904af2cd85eSAhmad Masri /* scan is supported on client interfaces and on AP interface */
9052be7d22fSVladimir Kondratiev switch (wdev->iftype) {
9062be7d22fSVladimir Kondratiev case NL80211_IFTYPE_STATION:
9072be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT:
9084332cac1SLior David case NL80211_IFTYPE_P2P_DEVICE:
909af2cd85eSAhmad Masri case NL80211_IFTYPE_AP:
9102be7d22fSVladimir Kondratiev break;
9112be7d22fSVladimir Kondratiev default:
9122be7d22fSVladimir Kondratiev return -EOPNOTSUPP;
9132be7d22fSVladimir Kondratiev }
9142be7d22fSVladimir Kondratiev
9152be7d22fSVladimir Kondratiev /* FW don't support scan after connection attempt */
9169419b6a2SVladimir Kondratiev if (test_bit(wil_status_dontscan, wil->status)) {
917e83eb2fcSVladimir Kondratiev wil_err(wil, "Can't scan now\n");
9182be7d22fSVladimir Kondratiev return -EBUSY;
9192be7d22fSVladimir Kondratiev }
9202be7d22fSVladimir Kondratiev
921bb6743f7SLior David mutex_lock(&wil->mutex);
922bb6743f7SLior David
923404bbb3cSLior David mutex_lock(&wil->vif_mutex);
924e00243faSLior David if (vif->scan_request || vif->p2p.discovery_started) {
925bb6743f7SLior David wil_err(wil, "Already scanning\n");
926404bbb3cSLior David mutex_unlock(&wil->vif_mutex);
927bb6743f7SLior David rc = -EAGAIN;
928bb6743f7SLior David goto out;
929bb6743f7SLior David }
930404bbb3cSLior David mutex_unlock(&wil->vif_mutex);
931bb6743f7SLior David
9324a0e45a7SLior David if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
933e00243faSLior David if (!wil->p2p_dev_started) {
934eb57a5b3SLior David wil_err(wil, "P2P search requested on stopped P2P device\n");
935bb6743f7SLior David rc = -EIO;
936bb6743f7SLior David goto out;
937eb57a5b3SLior David }
9384a0e45a7SLior David /* social scan on P2P_DEVICE is handled as p2p search */
9394a0e45a7SLior David if (wil_p2p_is_social_scan(request)) {
940e00243faSLior David vif->scan_request = request;
941e00243faSLior David if (vif->mid == 0)
9424332cac1SLior David wil->radio_wdev = wdev;
943e00243faSLior David rc = wil_p2p_search(vif, request);
9444332cac1SLior David if (rc) {
945e00243faSLior David if (vif->mid == 0)
946e00243faSLior David wil->radio_wdev =
947e00243faSLior David wil->main_ndev->ieee80211_ptr;
948e00243faSLior David vif->scan_request = NULL;
9494332cac1SLior David }
950bb6743f7SLior David goto out;
951e6d68341SDedy Lansky }
9524a0e45a7SLior David }
953e6d68341SDedy Lansky
954e00243faSLior David (void)wil_p2p_stop_discovery(vif);
955e6d68341SDedy Lansky
9562a91d7d0SVladimir Kondratiev wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
9578e52fe30SHamad Kadmany wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
9588e52fe30SHamad Kadmany
9598e52fe30SHamad Kadmany for (i = 0; i < request->n_ssids; i++) {
9608e52fe30SHamad Kadmany wil_dbg_misc(wil, "SSID[%d]", i);
9615eb443e9SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
9628e52fe30SHamad Kadmany request->ssids[i].ssid,
9635eb443e9SDedy Lansky request->ssids[i].ssid_len, true);
9648e52fe30SHamad Kadmany }
9658e52fe30SHamad Kadmany
9668e52fe30SHamad Kadmany if (request->n_ssids)
967e00243faSLior David rc = wmi_set_ssid(vif, request->ssids[0].ssid_len,
9688e52fe30SHamad Kadmany request->ssids[0].ssid);
9698e52fe30SHamad Kadmany else
970e00243faSLior David rc = wmi_set_ssid(vif, 0, NULL);
9718e52fe30SHamad Kadmany
9728e52fe30SHamad Kadmany if (rc) {
9738e52fe30SHamad Kadmany wil_err(wil, "set SSID for scan request failed: %d\n", rc);
974bb6743f7SLior David goto out;
9758e52fe30SHamad Kadmany }
9768e52fe30SHamad Kadmany
977e00243faSLior David vif->scan_request = request;
978e00243faSLior David mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO);
9792be7d22fSVladimir Kondratiev
9802be7d22fSVladimir Kondratiev memset(&cmd, 0, sizeof(cmd));
98174997a53SLior David cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
9822be7d22fSVladimir Kondratiev cmd.cmd.num_channels = 0;
9832be7d22fSVladimir Kondratiev n = min(request->n_channels, 4U);
9842be7d22fSVladimir Kondratiev for (i = 0; i < n; i++) {
9852be7d22fSVladimir Kondratiev int ch = request->channels[i]->hw_value;
9868fe59627SVladimir Kondratiev
9872be7d22fSVladimir Kondratiev if (ch == 0) {
9882be7d22fSVladimir Kondratiev wil_err(wil,
9892be7d22fSVladimir Kondratiev "Scan requested for unknown frequency %dMhz\n",
9902be7d22fSVladimir Kondratiev request->channels[i]->center_freq);
9912be7d22fSVladimir Kondratiev continue;
9922be7d22fSVladimir Kondratiev }
9932be7d22fSVladimir Kondratiev /* 0-based channel indexes */
9942be7d22fSVladimir Kondratiev cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
9957743882dSVladimir Kondratiev wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch,
9962be7d22fSVladimir Kondratiev request->channels[i]->center_freq);
9972be7d22fSVladimir Kondratiev }
9982be7d22fSVladimir Kondratiev
99977c91295SVladimir Kondratiev if (request->ie_len)
10005eb443e9SDedy Lansky wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1,
10015eb443e9SDedy Lansky request->ie, request->ie_len, true);
100277c91295SVladimir Kondratiev else
100377c91295SVladimir Kondratiev wil_dbg_misc(wil, "Scan has no IE's\n");
100477c91295SVladimir Kondratiev
1005e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
1006e00243faSLior David request->ie_len, request->ie);
10075421bf0cSVladimir Kondratiev if (rc)
1008bb6743f7SLior David goto out_restore;
100977c91295SVladimir Kondratiev
101074997a53SLior David if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
101174997a53SLior David cmd.cmd.discovery_mode = 1;
101274997a53SLior David wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
101374997a53SLior David }
101474997a53SLior David
1015e00243faSLior David if (vif->mid == 0)
10164332cac1SLior David wil->radio_wdev = wdev;
1017e00243faSLior David rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid,
1018e00243faSLior David &cmd, sizeof(cmd.cmd) +
10192be7d22fSVladimir Kondratiev cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
1020ed6f9dc6SVladimir Kondratiev
1021bb6743f7SLior David out_restore:
1022a2142086SVladimir Kondratiev if (rc) {
1023e00243faSLior David del_timer_sync(&vif->scan_timer);
1024e00243faSLior David if (vif->mid == 0)
1025e00243faSLior David wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
1026e00243faSLior David vif->scan_request = NULL;
1027a2142086SVladimir Kondratiev }
1028bb6743f7SLior David out:
1029bb6743f7SLior David mutex_unlock(&wil->mutex);
1030ed6f9dc6SVladimir Kondratiev return rc;
10312be7d22fSVladimir Kondratiev }
10322be7d22fSVladimir Kondratiev
wil_cfg80211_abort_scan(struct wiphy * wiphy,struct wireless_dev * wdev)1033035859a5SMaya Erez static void wil_cfg80211_abort_scan(struct wiphy *wiphy,
1034035859a5SMaya Erez struct wireless_dev *wdev)
1035035859a5SMaya Erez {
1036035859a5SMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1037e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
1038035859a5SMaya Erez
1039035859a5SMaya Erez wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
1040035859a5SMaya Erez
1041035859a5SMaya Erez mutex_lock(&wil->mutex);
1042404bbb3cSLior David mutex_lock(&wil->vif_mutex);
1043035859a5SMaya Erez
1044e00243faSLior David if (!vif->scan_request)
1045035859a5SMaya Erez goto out;
1046035859a5SMaya Erez
1047e00243faSLior David if (wdev != vif->scan_request->wdev) {
1048035859a5SMaya Erez wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
1049035859a5SMaya Erez goto out;
1050035859a5SMaya Erez }
1051035859a5SMaya Erez
1052e00243faSLior David if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev)
1053035859a5SMaya Erez wil_p2p_stop_radio_operations(wil);
1054035859a5SMaya Erez else
1055e00243faSLior David wil_abort_scan(vif, true);
1056035859a5SMaya Erez
1057035859a5SMaya Erez out:
1058404bbb3cSLior David mutex_unlock(&wil->vif_mutex);
1059035859a5SMaya Erez mutex_unlock(&wil->mutex);
1060035859a5SMaya Erez }
1061035859a5SMaya Erez
wil_print_crypto(struct wil6210_priv * wil,struct cfg80211_crypto_settings * c)1062feeac225SVladimir Kondratiev static void wil_print_crypto(struct wil6210_priv *wil,
1063feeac225SVladimir Kondratiev struct cfg80211_crypto_settings *c)
1064feeac225SVladimir Kondratiev {
1065feeac225SVladimir Kondratiev int i, n;
1066feeac225SVladimir Kondratiev
1067feeac225SVladimir Kondratiev wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
1068feeac225SVladimir Kondratiev c->wpa_versions, c->cipher_group);
1069feeac225SVladimir Kondratiev wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
1070feeac225SVladimir Kondratiev n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
1071feeac225SVladimir Kondratiev for (i = 0; i < n; i++)
1072feeac225SVladimir Kondratiev wil_dbg_misc(wil, " [%d] = 0x%08x\n", i,
1073feeac225SVladimir Kondratiev c->ciphers_pairwise[i]);
1074feeac225SVladimir Kondratiev wil_dbg_misc(wil, "}\n");
1075feeac225SVladimir Kondratiev wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
1076feeac225SVladimir Kondratiev n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
1077feeac225SVladimir Kondratiev for (i = 0; i < n; i++)
1078feeac225SVladimir Kondratiev wil_dbg_misc(wil, " [%d] = 0x%08x\n", i,
1079feeac225SVladimir Kondratiev c->akm_suites[i]);
1080feeac225SVladimir Kondratiev wil_dbg_misc(wil, "}\n");
1081feeac225SVladimir Kondratiev wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
1082feeac225SVladimir Kondratiev c->control_port, be16_to_cpu(c->control_port_ethertype),
1083feeac225SVladimir Kondratiev c->control_port_no_encrypt);
1084feeac225SVladimir Kondratiev }
1085feeac225SVladimir Kondratiev
1086b9010f10SAhmad Masri static const char *
wil_get_auth_type_name(enum nl80211_auth_type auth_type)1087b9010f10SAhmad Masri wil_get_auth_type_name(enum nl80211_auth_type auth_type)
1088b9010f10SAhmad Masri {
1089b9010f10SAhmad Masri switch (auth_type) {
1090b9010f10SAhmad Masri case NL80211_AUTHTYPE_OPEN_SYSTEM:
1091b9010f10SAhmad Masri return "OPEN_SYSTEM";
1092b9010f10SAhmad Masri case NL80211_AUTHTYPE_SHARED_KEY:
1093b9010f10SAhmad Masri return "SHARED_KEY";
1094b9010f10SAhmad Masri case NL80211_AUTHTYPE_FT:
1095b9010f10SAhmad Masri return "FT";
1096b9010f10SAhmad Masri case NL80211_AUTHTYPE_NETWORK_EAP:
1097b9010f10SAhmad Masri return "NETWORK_EAP";
1098b9010f10SAhmad Masri case NL80211_AUTHTYPE_SAE:
1099b9010f10SAhmad Masri return "SAE";
1100b9010f10SAhmad Masri case NL80211_AUTHTYPE_AUTOMATIC:
1101b9010f10SAhmad Masri return "AUTOMATIC";
1102b9010f10SAhmad Masri default:
1103b9010f10SAhmad Masri return "unknown";
1104b9010f10SAhmad Masri }
1105b9010f10SAhmad Masri }
wil_print_connect_params(struct wil6210_priv * wil,struct cfg80211_connect_params * sme)11068ca26163SVladimir Kondratiev static void wil_print_connect_params(struct wil6210_priv *wil,
11078ca26163SVladimir Kondratiev struct cfg80211_connect_params *sme)
11088ca26163SVladimir Kondratiev {
11098ca26163SVladimir Kondratiev wil_info(wil, "Connecting to:\n");
11108ca26163SVladimir Kondratiev if (sme->channel) {
11118ca26163SVladimir Kondratiev wil_info(wil, " Channel: %d freq %d\n",
11128ca26163SVladimir Kondratiev sme->channel->hw_value, sme->channel->center_freq);
11138ca26163SVladimir Kondratiev }
11148ca26163SVladimir Kondratiev if (sme->bssid)
11158ca26163SVladimir Kondratiev wil_info(wil, " BSSID: %pM\n", sme->bssid);
11168ca26163SVladimir Kondratiev if (sme->ssid)
11178ca26163SVladimir Kondratiev print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET,
11188ca26163SVladimir Kondratiev 16, 1, sme->ssid, sme->ssid_len, true);
1119b9010f10SAhmad Masri if (sme->prev_bssid)
1120b9010f10SAhmad Masri wil_info(wil, " Previous BSSID=%pM\n", sme->prev_bssid);
1121b9010f10SAhmad Masri wil_info(wil, " Auth Type: %s\n",
1122b9010f10SAhmad Masri wil_get_auth_type_name(sme->auth_type));
11238ca26163SVladimir Kondratiev wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open");
1124eabb03b4SLior David wil_info(wil, " PBSS: %d\n", sme->pbss);
1125feeac225SVladimir Kondratiev wil_print_crypto(wil, &sme->crypto);
11268ca26163SVladimir Kondratiev }
11278ca26163SVladimir Kondratiev
wil_ft_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)1128b9010f10SAhmad Masri static int wil_ft_connect(struct wiphy *wiphy,
1129b9010f10SAhmad Masri struct net_device *ndev,
1130b9010f10SAhmad Masri struct cfg80211_connect_params *sme)
1131b9010f10SAhmad Masri {
1132b9010f10SAhmad Masri struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1133b9010f10SAhmad Masri struct wil6210_vif *vif = ndev_to_vif(ndev);
1134b9010f10SAhmad Masri struct wmi_ft_auth_cmd auth_cmd;
1135b9010f10SAhmad Masri int rc;
1136b9010f10SAhmad Masri
1137b9010f10SAhmad Masri if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
1138b9010f10SAhmad Masri wil_err(wil, "FT: FW does not support FT roaming\n");
1139b9010f10SAhmad Masri return -EOPNOTSUPP;
1140b9010f10SAhmad Masri }
1141b9010f10SAhmad Masri
1142b9010f10SAhmad Masri if (!sme->prev_bssid) {
1143b9010f10SAhmad Masri wil_err(wil, "FT: prev_bssid was not set\n");
1144b9010f10SAhmad Masri return -EINVAL;
1145b9010f10SAhmad Masri }
1146b9010f10SAhmad Masri
1147b9010f10SAhmad Masri if (ether_addr_equal(sme->prev_bssid, sme->bssid)) {
1148b9010f10SAhmad Masri wil_err(wil, "FT: can not roam to same AP\n");
1149b9010f10SAhmad Masri return -EINVAL;
1150b9010f10SAhmad Masri }
1151b9010f10SAhmad Masri
1152b9010f10SAhmad Masri if (!test_bit(wil_vif_fwconnected, vif->status)) {
1153b9010f10SAhmad Masri wil_err(wil, "FT: roam while not connected\n");
1154b9010f10SAhmad Masri return -EINVAL;
1155b9010f10SAhmad Masri }
1156b9010f10SAhmad Masri
1157b9010f10SAhmad Masri if (vif->privacy != sme->privacy) {
1158b9010f10SAhmad Masri wil_err(wil, "FT: privacy mismatch, current (%d) roam (%d)\n",
1159b9010f10SAhmad Masri vif->privacy, sme->privacy);
1160b9010f10SAhmad Masri return -EINVAL;
1161b9010f10SAhmad Masri }
1162b9010f10SAhmad Masri
1163b9010f10SAhmad Masri if (sme->pbss) {
1164b9010f10SAhmad Masri wil_err(wil, "FT: roam is not valid for PBSS\n");
1165b9010f10SAhmad Masri return -EINVAL;
1166b9010f10SAhmad Masri }
1167b9010f10SAhmad Masri
1168b9010f10SAhmad Masri memset(&auth_cmd, 0, sizeof(auth_cmd));
1169b9010f10SAhmad Masri auth_cmd.channel = sme->channel->hw_value - 1;
1170b9010f10SAhmad Masri ether_addr_copy(auth_cmd.bssid, sme->bssid);
1171b9010f10SAhmad Masri
1172b9010f10SAhmad Masri wil_info(wil, "FT: roaming\n");
1173b9010f10SAhmad Masri
1174b9010f10SAhmad Masri set_bit(wil_vif_ft_roam, vif->status);
1175b9010f10SAhmad Masri rc = wmi_send(wil, WMI_FT_AUTH_CMDID, vif->mid,
1176b9010f10SAhmad Masri &auth_cmd, sizeof(auth_cmd));
1177b9010f10SAhmad Masri if (rc == 0)
1178b9010f10SAhmad Masri mod_timer(&vif->connect_timer,
1179b9010f10SAhmad Masri jiffies + msecs_to_jiffies(5000));
1180b9010f10SAhmad Masri else
1181b9010f10SAhmad Masri clear_bit(wil_vif_ft_roam, vif->status);
1182b9010f10SAhmad Masri
1183b9010f10SAhmad Masri return rc;
1184b9010f10SAhmad Masri }
1185b9010f10SAhmad Masri
wil_get_wmi_edmg_channel(struct wil6210_priv * wil,u8 edmg_bw_config,u8 edmg_channels,u8 * wmi_ch)11869abe3e30SAlexei Avshalom Lazar static int wil_get_wmi_edmg_channel(struct wil6210_priv *wil, u8 edmg_bw_config,
11879abe3e30SAlexei Avshalom Lazar u8 edmg_channels, u8 *wmi_ch)
11889abe3e30SAlexei Avshalom Lazar {
11899abe3e30SAlexei Avshalom Lazar if (!edmg_bw_config) {
11909abe3e30SAlexei Avshalom Lazar *wmi_ch = 0;
11919abe3e30SAlexei Avshalom Lazar return 0;
11929abe3e30SAlexei Avshalom Lazar } else if (edmg_bw_config == WIL_EDMG_BW_CONFIGURATION) {
11939abe3e30SAlexei Avshalom Lazar /* convert from edmg channel bitmap into edmg channel number */
11949abe3e30SAlexei Avshalom Lazar switch (edmg_channels) {
11959abe3e30SAlexei Avshalom Lazar case WIL_EDMG_CHANNEL_9_SUBCHANNELS:
11969abe3e30SAlexei Avshalom Lazar return wil_spec2wmi_ch(9, wmi_ch);
11979abe3e30SAlexei Avshalom Lazar case WIL_EDMG_CHANNEL_10_SUBCHANNELS:
11989abe3e30SAlexei Avshalom Lazar return wil_spec2wmi_ch(10, wmi_ch);
11999abe3e30SAlexei Avshalom Lazar case WIL_EDMG_CHANNEL_11_SUBCHANNELS:
12009abe3e30SAlexei Avshalom Lazar return wil_spec2wmi_ch(11, wmi_ch);
12019abe3e30SAlexei Avshalom Lazar default:
12029abe3e30SAlexei Avshalom Lazar wil_err(wil, "Unsupported edmg channel bitmap 0x%x\n",
12039abe3e30SAlexei Avshalom Lazar edmg_channels);
12049abe3e30SAlexei Avshalom Lazar return -EINVAL;
12059abe3e30SAlexei Avshalom Lazar }
12069abe3e30SAlexei Avshalom Lazar } else {
12079abe3e30SAlexei Avshalom Lazar wil_err(wil, "Unsupported EDMG BW configuration %d\n",
12089abe3e30SAlexei Avshalom Lazar edmg_bw_config);
12099abe3e30SAlexei Avshalom Lazar return -EINVAL;
12109abe3e30SAlexei Avshalom Lazar }
12119abe3e30SAlexei Avshalom Lazar }
12129abe3e30SAlexei Avshalom Lazar
wil_cfg80211_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)12132be7d22fSVladimir Kondratiev static int wil_cfg80211_connect(struct wiphy *wiphy,
12142be7d22fSVladimir Kondratiev struct net_device *ndev,
12152be7d22fSVladimir Kondratiev struct cfg80211_connect_params *sme)
12162be7d22fSVladimir Kondratiev {
12172be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1218e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
12192be7d22fSVladimir Kondratiev struct cfg80211_bss *bss;
12202be7d22fSVladimir Kondratiev struct wmi_connect_cmd conn;
12212be7d22fSVladimir Kondratiev const u8 *ssid_eid;
12222be7d22fSVladimir Kondratiev const u8 *rsn_eid;
12232be7d22fSVladimir Kondratiev int ch;
12242be7d22fSVladimir Kondratiev int rc = 0;
1225b9010f10SAhmad Masri bool is_ft_roam = false;
1226b9010f10SAhmad Masri u8 network_type;
1227eabb03b4SLior David enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
12282be7d22fSVladimir Kondratiev
1229e00243faSLior David wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
1230344a7024SVladimir Kondratiev wil_print_connect_params(wil, sme);
1231344a7024SVladimir Kondratiev
1232b9010f10SAhmad Masri if (sme->auth_type == NL80211_AUTHTYPE_FT)
1233b9010f10SAhmad Masri is_ft_roam = true;
1234b9010f10SAhmad Masri if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC &&
1235b9010f10SAhmad Masri test_bit(wil_vif_fwconnected, vif->status))
1236b9010f10SAhmad Masri is_ft_roam = true;
1237b9010f10SAhmad Masri
1238b9010f10SAhmad Masri if (!is_ft_roam)
12395bd60982SLior David if (test_bit(wil_vif_fwconnecting, vif->status) ||
12405bd60982SLior David test_bit(wil_vif_fwconnected, vif->status))
12414cd9e837SVladimir Kondratiev return -EALREADY;
12424cd9e837SVladimir Kondratiev
1243344a7024SVladimir Kondratiev if (sme->ie_len > WMI_MAX_IE_LEN) {
1244344a7024SVladimir Kondratiev wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
1245344a7024SVladimir Kondratiev return -ERANGE;
1246344a7024SVladimir Kondratiev }
1247344a7024SVladimir Kondratiev
1248344a7024SVladimir Kondratiev rsn_eid = sme->ie ?
1249344a7024SVladimir Kondratiev cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
1250344a7024SVladimir Kondratiev NULL;
1251b9010f10SAhmad Masri if (sme->privacy && !rsn_eid) {
125227aa6b71SVladimir Kondratiev wil_info(wil, "WSC connection\n");
1253b9010f10SAhmad Masri if (is_ft_roam) {
1254b9010f10SAhmad Masri wil_err(wil, "No WSC with FT roam\n");
1255b9010f10SAhmad Masri return -EINVAL;
1256b9010f10SAhmad Masri }
1257b9010f10SAhmad Masri }
12588ca26163SVladimir Kondratiev
1259eabb03b4SLior David if (sme->pbss)
1260eabb03b4SLior David bss_type = IEEE80211_BSS_TYPE_PBSS;
126134d50519SLior David
12622be7d22fSVladimir Kondratiev bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
12632be7d22fSVladimir Kondratiev sme->ssid, sme->ssid_len,
1264eabb03b4SLior David bss_type, IEEE80211_PRIVACY_ANY);
12652be7d22fSVladimir Kondratiev if (!bss) {
12662be7d22fSVladimir Kondratiev wil_err(wil, "Unable to find BSS\n");
12672be7d22fSVladimir Kondratiev return -ENOENT;
12682be7d22fSVladimir Kondratiev }
12692be7d22fSVladimir Kondratiev
12702be7d22fSVladimir Kondratiev ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
12712be7d22fSVladimir Kondratiev if (!ssid_eid) {
12722be7d22fSVladimir Kondratiev wil_err(wil, "No SSID\n");
12732be7d22fSVladimir Kondratiev rc = -ENOENT;
12742be7d22fSVladimir Kondratiev goto out;
12752be7d22fSVladimir Kondratiev }
1276e00243faSLior David vif->privacy = sme->privacy;
1277e00243faSLior David vif->pbss = sme->pbss;
12782be7d22fSVladimir Kondratiev
1279b9010f10SAhmad Masri rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
1280b9010f10SAhmad Masri if (rc)
1281b9010f10SAhmad Masri goto out;
1282b9010f10SAhmad Masri
1283b9010f10SAhmad Masri switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
1284b9010f10SAhmad Masri case WLAN_CAPABILITY_DMG_TYPE_AP:
1285b9010f10SAhmad Masri network_type = WMI_NETTYPE_INFRA;
1286b9010f10SAhmad Masri break;
1287b9010f10SAhmad Masri case WLAN_CAPABILITY_DMG_TYPE_PBSS:
1288b9010f10SAhmad Masri network_type = WMI_NETTYPE_P2P;
1289b9010f10SAhmad Masri break;
1290b9010f10SAhmad Masri default:
1291b9010f10SAhmad Masri wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
1292b9010f10SAhmad Masri bss->capability);
1293b9010f10SAhmad Masri rc = -EINVAL;
1294b9010f10SAhmad Masri goto out;
1295b9010f10SAhmad Masri }
1296b9010f10SAhmad Masri
1297b9010f10SAhmad Masri ch = bss->channel->hw_value;
1298b9010f10SAhmad Masri if (ch == 0) {
1299b9010f10SAhmad Masri wil_err(wil, "BSS at unknown frequency %dMhz\n",
1300b9010f10SAhmad Masri bss->channel->center_freq);
1301b9010f10SAhmad Masri rc = -EOPNOTSUPP;
1302b9010f10SAhmad Masri goto out;
1303b9010f10SAhmad Masri }
1304b9010f10SAhmad Masri
1305b9010f10SAhmad Masri if (is_ft_roam) {
1306b9010f10SAhmad Masri if (network_type != WMI_NETTYPE_INFRA) {
1307b9010f10SAhmad Masri wil_err(wil, "FT: Unsupported BSS type, capability= 0x%04x\n",
1308b9010f10SAhmad Masri bss->capability);
1309b9010f10SAhmad Masri rc = -EINVAL;
1310b9010f10SAhmad Masri goto out;
1311b9010f10SAhmad Masri }
1312b9010f10SAhmad Masri rc = wil_ft_connect(wiphy, ndev, sme);
1313b9010f10SAhmad Masri if (rc == 0)
1314b9010f10SAhmad Masri vif->bss = bss;
1315b9010f10SAhmad Masri goto out;
1316b9010f10SAhmad Masri }
1317b9010f10SAhmad Masri
1318e00243faSLior David if (vif->privacy) {
1319230d8442SVladimir Kondratiev /* For secure assoc, remove old keys */
1320e00243faSLior David rc = wmi_del_cipher_key(vif, 0, bss->bssid,
1321230d8442SVladimir Kondratiev WMI_KEY_USE_PAIRWISE);
13222be7d22fSVladimir Kondratiev if (rc) {
1323230d8442SVladimir Kondratiev wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
1324230d8442SVladimir Kondratiev goto out;
1325230d8442SVladimir Kondratiev }
1326e00243faSLior David rc = wmi_del_cipher_key(vif, 0, bss->bssid,
1327230d8442SVladimir Kondratiev WMI_KEY_USE_RX_GROUP);
1328230d8442SVladimir Kondratiev if (rc) {
1329230d8442SVladimir Kondratiev wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
13302be7d22fSVladimir Kondratiev goto out;
13312be7d22fSVladimir Kondratiev }
1332ac4acdb7SVladimir Kondratiev }
1333ac4acdb7SVladimir Kondratiev
13342be7d22fSVladimir Kondratiev /* WMI_CONNECT_CMD */
13352be7d22fSVladimir Kondratiev memset(&conn, 0, sizeof(conn));
1336b9010f10SAhmad Masri conn.network_type = network_type;
1337e00243faSLior David if (vif->privacy) {
133827aa6b71SVladimir Kondratiev if (rsn_eid) { /* regular secure connection */
13392be7d22fSVladimir Kondratiev conn.dot11_auth_mode = WMI_AUTH11_SHARED;
13402be7d22fSVladimir Kondratiev conn.auth_mode = WMI_AUTH_WPA2_PSK;
13412be7d22fSVladimir Kondratiev conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
13422be7d22fSVladimir Kondratiev conn.pairwise_crypto_len = 16;
1343230d8442SVladimir Kondratiev conn.group_crypto_type = WMI_CRYPT_AES_GCMP;
1344230d8442SVladimir Kondratiev conn.group_crypto_len = 16;
134527aa6b71SVladimir Kondratiev } else { /* WSC */
134627aa6b71SVladimir Kondratiev conn.dot11_auth_mode = WMI_AUTH11_WSC;
134727aa6b71SVladimir Kondratiev conn.auth_mode = WMI_AUTH_NONE;
134827aa6b71SVladimir Kondratiev }
134927aa6b71SVladimir Kondratiev } else { /* insecure connection */
13502be7d22fSVladimir Kondratiev conn.dot11_auth_mode = WMI_AUTH11_OPEN;
13512be7d22fSVladimir Kondratiev conn.auth_mode = WMI_AUTH_NONE;
13522be7d22fSVladimir Kondratiev }
13532be7d22fSVladimir Kondratiev
13542be7d22fSVladimir Kondratiev conn.ssid_len = min_t(u8, ssid_eid[1], 32);
13552be7d22fSVladimir Kondratiev memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
13562be7d22fSVladimir Kondratiev conn.channel = ch - 1;
13572be7d22fSVladimir Kondratiev
13589abe3e30SAlexei Avshalom Lazar rc = wil_get_wmi_edmg_channel(wil, sme->edmg.bw_config,
13599abe3e30SAlexei Avshalom Lazar sme->edmg.channels, &conn.edmg_channel);
13609abe3e30SAlexei Avshalom Lazar if (rc < 0)
13619abe3e30SAlexei Avshalom Lazar return rc;
13629abe3e30SAlexei Avshalom Lazar
1363a82553bbSVladimir Kondratiev ether_addr_copy(conn.bssid, bss->bssid);
1364a82553bbSVladimir Kondratiev ether_addr_copy(conn.dst_mac, bss->bssid);
1365e83eb2fcSVladimir Kondratiev
13665bd60982SLior David set_bit(wil_vif_fwconnecting, vif->status);
13672be7d22fSVladimir Kondratiev
1368e00243faSLior David rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
13692be7d22fSVladimir Kondratiev if (rc == 0) {
1370c5e96c91SDedy Lansky netif_carrier_on(ndev);
13715bd60982SLior David if (!wil_has_other_active_ifaces(wil, ndev, false, true))
13729953a782SLior David wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
1373e00243faSLior David vif->bss = bss;
13742be7d22fSVladimir Kondratiev /* Connect can take lots of time */
1375e00243faSLior David mod_timer(&vif->connect_timer,
1376d83ad4c4SLior David jiffies + msecs_to_jiffies(5000));
1377b338f74eSVladimir Kondratiev } else {
13785bd60982SLior David clear_bit(wil_vif_fwconnecting, vif->status);
13792be7d22fSVladimir Kondratiev }
13802be7d22fSVladimir Kondratiev
13812be7d22fSVladimir Kondratiev out:
13825b112d3dSJohannes Berg cfg80211_put_bss(wiphy, bss);
13832be7d22fSVladimir Kondratiev
13842be7d22fSVladimir Kondratiev return rc;
13852be7d22fSVladimir Kondratiev }
13862be7d22fSVladimir Kondratiev
wil_cfg80211_disconnect(struct wiphy * wiphy,struct net_device * ndev,u16 reason_code)13872be7d22fSVladimir Kondratiev static int wil_cfg80211_disconnect(struct wiphy *wiphy,
13882be7d22fSVladimir Kondratiev struct net_device *ndev,
13892be7d22fSVladimir Kondratiev u16 reason_code)
13902be7d22fSVladimir Kondratiev {
13912be7d22fSVladimir Kondratiev int rc;
13922be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1393e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
13942be7d22fSVladimir Kondratiev
1395e00243faSLior David wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
1396e00243faSLior David reason_code, vif->mid);
1397de9084efSVladimir Kondratiev
13985bd60982SLior David if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
13995bd60982SLior David test_bit(wil_vif_fwconnected, vif->status))) {
1400af3db60aSLazar Alexei wil_err(wil, "Disconnect was called while disconnected\n");
140178771d76SVladimir Kondratiev return 0;
140278771d76SVladimir Kondratiev }
140378771d76SVladimir Kondratiev
1404e00243faSLior David vif->locally_generated_disc = true;
1405e00243faSLior David rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
140678771d76SVladimir Kondratiev WMI_DISCONNECT_EVENTID, NULL, 0,
140778771d76SVladimir Kondratiev WIL6210_DISCONNECT_TO_MS);
140878771d76SVladimir Kondratiev if (rc)
1409af3db60aSLazar Alexei wil_err(wil, "disconnect error %d\n", rc);
14102be7d22fSVladimir Kondratiev
14112be7d22fSVladimir Kondratiev return rc;
14122be7d22fSVladimir Kondratiev }
14132be7d22fSVladimir Kondratiev
wil_cfg80211_set_wiphy_params(struct wiphy * wiphy,u32 changed)14143fea18d0SLior David static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
14153fea18d0SLior David {
14163fea18d0SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy);
14173fea18d0SLior David int rc;
14183fea18d0SLior David
14193fea18d0SLior David /* these parameters are explicitly not supported */
14203fea18d0SLior David if (changed & (WIPHY_PARAM_RETRY_LONG |
14213fea18d0SLior David WIPHY_PARAM_FRAG_THRESHOLD |
14223fea18d0SLior David WIPHY_PARAM_RTS_THRESHOLD))
14233fea18d0SLior David return -ENOTSUPP;
14243fea18d0SLior David
14253fea18d0SLior David if (changed & WIPHY_PARAM_RETRY_SHORT) {
14263fea18d0SLior David rc = wmi_set_mgmt_retry(wil, wiphy->retry_short);
14273fea18d0SLior David if (rc)
14283fea18d0SLior David return rc;
14293fea18d0SLior David }
14303fea18d0SLior David
14313fea18d0SLior David return 0;
14323fea18d0SLior David }
14333fea18d0SLior David
wil_cfg80211_mgmt_tx(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_mgmt_tx_params * params,u64 * cookie)14340b39aaf2SVladimir Kondratiev int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
14351647f12fSVladimir Kondratiev struct cfg80211_mgmt_tx_params *params,
14361647f12fSVladimir Kondratiev u64 *cookie)
14371647f12fSVladimir Kondratiev {
14381647f12fSVladimir Kondratiev const u8 *buf = params->buf;
14391c21cc5fSDedy Lansky size_t len = params->len;
14401647f12fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1441e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
14421647f12fSVladimir Kondratiev int rc;
14431c21cc5fSDedy Lansky bool tx_status;
14441647f12fSVladimir Kondratiev
1445b698e2dfSAhmad Masri wil_dbg_misc(wil, "mgmt_tx: channel %d offchan %d, wait %d\n",
1446b698e2dfSAhmad Masri params->chan ? params->chan->hw_value : -1,
1447b698e2dfSAhmad Masri params->offchan,
1448b698e2dfSAhmad Masri params->wait);
1449b698e2dfSAhmad Masri
1450b698e2dfSAhmad Masri /* Note, currently we support the "wait" parameter only on AP mode.
1451b698e2dfSAhmad Masri * In other modes, user-space must call remain_on_channel before
1452b698e2dfSAhmad Masri * mgmt_tx or listen on a channel other than active one.
1453e6d68341SDedy Lansky */
1454e6d68341SDedy Lansky
1455b698e2dfSAhmad Masri if (params->chan && params->chan->hw_value == 0) {
1456b698e2dfSAhmad Masri wil_err(wil, "invalid channel\n");
1457b698e2dfSAhmad Masri return -EINVAL;
1458b698e2dfSAhmad Masri }
1459e6d68341SDedy Lansky
1460b698e2dfSAhmad Masri if (wdev->iftype != NL80211_IFTYPE_AP) {
1461b698e2dfSAhmad Masri wil_dbg_misc(wil,
1462b698e2dfSAhmad Masri "send WMI_SW_TX_REQ_CMDID on non-AP interfaces\n");
1463b698e2dfSAhmad Masri rc = wmi_mgmt_tx(vif, buf, len);
1464b698e2dfSAhmad Masri goto out;
1465b698e2dfSAhmad Masri }
1466b698e2dfSAhmad Masri
1467b698e2dfSAhmad Masri if (!params->chan || params->chan->hw_value == vif->channel) {
1468b698e2dfSAhmad Masri wil_dbg_misc(wil,
1469b698e2dfSAhmad Masri "send WMI_SW_TX_REQ_CMDID for on-channel\n");
1470b698e2dfSAhmad Masri rc = wmi_mgmt_tx(vif, buf, len);
1471b698e2dfSAhmad Masri goto out;
1472b698e2dfSAhmad Masri }
1473b698e2dfSAhmad Masri
1474b698e2dfSAhmad Masri if (params->offchan == 0) {
1475b698e2dfSAhmad Masri wil_err(wil,
1476b698e2dfSAhmad Masri "invalid channel params: current %d requested %d, off-channel not allowed\n",
1477b698e2dfSAhmad Masri vif->channel, params->chan->hw_value);
1478b698e2dfSAhmad Masri return -EBUSY;
1479b698e2dfSAhmad Masri }
1480b698e2dfSAhmad Masri
1481b698e2dfSAhmad Masri /* use the wmi_mgmt_tx_ext only on AP mode and off-channel */
1482b698e2dfSAhmad Masri rc = wmi_mgmt_tx_ext(vif, buf, len, params->chan->hw_value,
1483b698e2dfSAhmad Masri params->wait);
1484b698e2dfSAhmad Masri
1485b698e2dfSAhmad Masri out:
148649122ec4SLior David /* when the sent packet was not acked by receiver(ACK=0), rc will
148749122ec4SLior David * be -EAGAIN. In this case this function needs to return success,
148849122ec4SLior David * the ACK=0 will be reflected in tx_status.
148949122ec4SLior David */
1490b698e2dfSAhmad Masri tx_status = (rc == 0);
149149122ec4SLior David rc = (rc == -EAGAIN) ? 0 : rc;
1492304464f4SVladimir Kondratiev cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
1493304464f4SVladimir Kondratiev tx_status, GFP_KERNEL);
1494b698e2dfSAhmad Masri
14951647f12fSVladimir Kondratiev return rc;
14961647f12fSVladimir Kondratiev }
14971647f12fSVladimir Kondratiev
wil_cfg80211_set_channel(struct wiphy * wiphy,struct cfg80211_chan_def * chandef)14982be7d22fSVladimir Kondratiev static int wil_cfg80211_set_channel(struct wiphy *wiphy,
14992be7d22fSVladimir Kondratiev struct cfg80211_chan_def *chandef)
15002be7d22fSVladimir Kondratiev {
15012be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
15022be7d22fSVladimir Kondratiev
15037d3e4dbeSLior David wil->monitor_chandef = *chandef;
15042be7d22fSVladimir Kondratiev
15052be7d22fSVladimir Kondratiev return 0;
15062be7d22fSVladimir Kondratiev }
15072be7d22fSVladimir Kondratiev
wil_detect_key_usage(struct wireless_dev * wdev,bool pairwise)1508e00243faSLior David static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev,
1509230d8442SVladimir Kondratiev bool pairwise)
1510230d8442SVladimir Kondratiev {
1511e00243faSLior David struct wil6210_priv *wil = wdev_to_wil(wdev);
1512230d8442SVladimir Kondratiev enum wmi_key_usage rc;
1513230d8442SVladimir Kondratiev
1514230d8442SVladimir Kondratiev if (pairwise) {
1515230d8442SVladimir Kondratiev rc = WMI_KEY_USE_PAIRWISE;
1516230d8442SVladimir Kondratiev } else {
1517230d8442SVladimir Kondratiev switch (wdev->iftype) {
1518230d8442SVladimir Kondratiev case NL80211_IFTYPE_STATION:
1519e6d68341SDedy Lansky case NL80211_IFTYPE_P2P_CLIENT:
1520230d8442SVladimir Kondratiev rc = WMI_KEY_USE_RX_GROUP;
1521230d8442SVladimir Kondratiev break;
1522230d8442SVladimir Kondratiev case NL80211_IFTYPE_AP:
1523e6d68341SDedy Lansky case NL80211_IFTYPE_P2P_GO:
1524230d8442SVladimir Kondratiev rc = WMI_KEY_USE_TX_GROUP;
1525230d8442SVladimir Kondratiev break;
1526230d8442SVladimir Kondratiev default:
1527230d8442SVladimir Kondratiev /* TODO: Rx GTK or Tx GTK? */
1528230d8442SVladimir Kondratiev wil_err(wil, "Can't determine GTK type\n");
1529230d8442SVladimir Kondratiev rc = WMI_KEY_USE_RX_GROUP;
1530230d8442SVladimir Kondratiev break;
1531230d8442SVladimir Kondratiev }
1532230d8442SVladimir Kondratiev }
1533af3db60aSLazar Alexei wil_dbg_misc(wil, "detect_key_usage: -> %s\n", key_usage_str[rc]);
1534230d8442SVladimir Kondratiev
1535230d8442SVladimir Kondratiev return rc;
1536230d8442SVladimir Kondratiev }
1537230d8442SVladimir Kondratiev
153874b6ac58SMaya Erez static struct wil_sta_info *
wil_find_sta_by_key_usage(struct wil6210_priv * wil,u8 mid,enum wmi_key_usage key_usage,const u8 * mac_addr)1539e00243faSLior David wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
154058527421SVladimir Kondratiev enum wmi_key_usage key_usage, const u8 *mac_addr)
154158527421SVladimir Kondratiev {
154258527421SVladimir Kondratiev int cid = -EINVAL;
154358527421SVladimir Kondratiev
154458527421SVladimir Kondratiev if (key_usage == WMI_KEY_USE_TX_GROUP)
154558527421SVladimir Kondratiev return NULL; /* not needed */
154658527421SVladimir Kondratiev
154758527421SVladimir Kondratiev /* supplicant provides Rx group key in STA mode with NULL MAC address */
154858527421SVladimir Kondratiev if (mac_addr)
1549e00243faSLior David cid = wil_find_cid(wil, mid, mac_addr);
155058527421SVladimir Kondratiev else if (key_usage == WMI_KEY_USE_RX_GROUP)
1551e00243faSLior David cid = wil_find_cid_by_idx(wil, mid, 0);
155258527421SVladimir Kondratiev if (cid < 0) {
155374b6ac58SMaya Erez wil_err(wil, "No CID for %pM %s\n", mac_addr,
155474b6ac58SMaya Erez key_usage_str[key_usage]);
155558527421SVladimir Kondratiev return ERR_PTR(cid);
155658527421SVladimir Kondratiev }
155758527421SVladimir Kondratiev
155874b6ac58SMaya Erez return &wil->sta[cid];
155974b6ac58SMaya Erez }
156058527421SVladimir Kondratiev
wil_set_crypto_rx(u8 key_index,enum wmi_key_usage key_usage,struct wil_sta_info * cs,struct key_params * params)1561b9010f10SAhmad Masri void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
156274b6ac58SMaya Erez struct wil_sta_info *cs,
156374b6ac58SMaya Erez struct key_params *params)
156474b6ac58SMaya Erez {
156574b6ac58SMaya Erez struct wil_tid_crypto_rx_single *cc;
156674b6ac58SMaya Erez int tid;
156774b6ac58SMaya Erez
156874b6ac58SMaya Erez if (!cs)
156974b6ac58SMaya Erez return;
157074b6ac58SMaya Erez
157174b6ac58SMaya Erez switch (key_usage) {
157242fe1e51SAhmad Masri case WMI_KEY_USE_STORE_PTK:
157374b6ac58SMaya Erez case WMI_KEY_USE_PAIRWISE:
157474b6ac58SMaya Erez for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
157574b6ac58SMaya Erez cc = &cs->tid_crypto_rx[tid].key_id[key_index];
157674b6ac58SMaya Erez if (params->seq)
157774b6ac58SMaya Erez memcpy(cc->pn, params->seq,
157874b6ac58SMaya Erez IEEE80211_GCMP_PN_LEN);
157974b6ac58SMaya Erez else
158074b6ac58SMaya Erez memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
158174b6ac58SMaya Erez cc->key_set = true;
158274b6ac58SMaya Erez }
158374b6ac58SMaya Erez break;
158474b6ac58SMaya Erez case WMI_KEY_USE_RX_GROUP:
158574b6ac58SMaya Erez cc = &cs->group_crypto_rx.key_id[key_index];
158674b6ac58SMaya Erez if (params->seq)
158774b6ac58SMaya Erez memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
158874b6ac58SMaya Erez else
158974b6ac58SMaya Erez memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
159074b6ac58SMaya Erez cc->key_set = true;
159174b6ac58SMaya Erez break;
159274b6ac58SMaya Erez default:
159374b6ac58SMaya Erez break;
159474b6ac58SMaya Erez }
159574b6ac58SMaya Erez }
159674b6ac58SMaya Erez
wil_del_rx_key(u8 key_index,enum wmi_key_usage key_usage,struct wil_sta_info * cs)159774b6ac58SMaya Erez static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
159874b6ac58SMaya Erez struct wil_sta_info *cs)
159974b6ac58SMaya Erez {
160074b6ac58SMaya Erez struct wil_tid_crypto_rx_single *cc;
160174b6ac58SMaya Erez int tid;
160274b6ac58SMaya Erez
160374b6ac58SMaya Erez if (!cs)
160474b6ac58SMaya Erez return;
160574b6ac58SMaya Erez
160674b6ac58SMaya Erez switch (key_usage) {
160774b6ac58SMaya Erez case WMI_KEY_USE_PAIRWISE:
160874b6ac58SMaya Erez for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
160974b6ac58SMaya Erez cc = &cs->tid_crypto_rx[tid].key_id[key_index];
161074b6ac58SMaya Erez cc->key_set = false;
161174b6ac58SMaya Erez }
161274b6ac58SMaya Erez break;
161374b6ac58SMaya Erez case WMI_KEY_USE_RX_GROUP:
161474b6ac58SMaya Erez cc = &cs->group_crypto_rx.key_id[key_index];
161574b6ac58SMaya Erez cc->key_set = false;
161674b6ac58SMaya Erez break;
161774b6ac58SMaya Erez default:
161874b6ac58SMaya Erez break;
161974b6ac58SMaya Erez }
162058527421SVladimir Kondratiev }
162158527421SVladimir Kondratiev
wil_cfg80211_add_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_index,bool pairwise,const u8 * mac_addr,struct key_params * params)16222be7d22fSVladimir Kondratiev static int wil_cfg80211_add_key(struct wiphy *wiphy,
1623*e7a7b84eSVeerendranath Jakkam struct net_device *ndev, int link_id,
16242be7d22fSVladimir Kondratiev u8 key_index, bool pairwise,
16252be7d22fSVladimir Kondratiev const u8 *mac_addr,
16262be7d22fSVladimir Kondratiev struct key_params *params)
16272be7d22fSVladimir Kondratiev {
162858527421SVladimir Kondratiev int rc;
1629e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
16302be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1631e00243faSLior David struct wireless_dev *wdev = vif_to_wdev(vif);
1632e00243faSLior David enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
1633e00243faSLior David struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
1634e00243faSLior David key_usage,
163558527421SVladimir Kondratiev mac_addr);
16362be7d22fSVladimir Kondratiev
163774b6ac58SMaya Erez if (!params) {
163874b6ac58SMaya Erez wil_err(wil, "NULL params\n");
163974b6ac58SMaya Erez return -EINVAL;
164074b6ac58SMaya Erez }
164174b6ac58SMaya Erez
1642af3db60aSLazar Alexei wil_dbg_misc(wil, "add_key: %pM %s[%d] PN %*phN\n",
164358527421SVladimir Kondratiev mac_addr, key_usage_str[key_usage], key_index,
164458527421SVladimir Kondratiev params->seq_len, params->seq);
1645db8adcbfSVladimir Kondratiev
164674b6ac58SMaya Erez if (IS_ERR(cs)) {
1647b9010f10SAhmad Masri /* in FT, sta info may not be available as add_key may be
1648b9010f10SAhmad Masri * sent by host before FW sends WMI_CONNECT_EVENT
1649b9010f10SAhmad Masri */
1650b9010f10SAhmad Masri if (!test_bit(wil_vif_ft_roam, vif->status)) {
1651af3db60aSLazar Alexei wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
1652af3db60aSLazar Alexei mac_addr, key_usage_str[key_usage], key_index,
165358527421SVladimir Kondratiev params->seq_len, params->seq);
165458527421SVladimir Kondratiev return -EINVAL;
165558527421SVladimir Kondratiev }
1656a5f3aed5SWan Jiabing } else {
165774b6ac58SMaya Erez wil_del_rx_key(key_index, key_usage, cs);
1658a5f3aed5SWan Jiabing }
165958527421SVladimir Kondratiev
166058527421SVladimir Kondratiev if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
166158527421SVladimir Kondratiev wil_err(wil,
1662af3db60aSLazar Alexei "Wrong PN len %d, %pM %s[%d] PN %*phN\n",
1663af3db60aSLazar Alexei params->seq_len, mac_addr,
166458527421SVladimir Kondratiev key_usage_str[key_usage], key_index,
166558527421SVladimir Kondratiev params->seq_len, params->seq);
166658527421SVladimir Kondratiev return -EINVAL;
166758527421SVladimir Kondratiev }
166858527421SVladimir Kondratiev
166942fe1e51SAhmad Masri spin_lock_bh(&wil->eap_lock);
167042fe1e51SAhmad Masri if (pairwise && wdev->iftype == NL80211_IFTYPE_STATION &&
167142fe1e51SAhmad Masri (vif->ptk_rekey_state == WIL_REKEY_M3_RECEIVED ||
167242fe1e51SAhmad Masri vif->ptk_rekey_state == WIL_REKEY_WAIT_M4_SENT)) {
167342fe1e51SAhmad Masri key_usage = WMI_KEY_USE_STORE_PTK;
167442fe1e51SAhmad Masri vif->ptk_rekey_state = WIL_REKEY_WAIT_M4_SENT;
167542fe1e51SAhmad Masri wil_dbg_misc(wil, "Store EAPOL key\n");
167642fe1e51SAhmad Masri }
167742fe1e51SAhmad Masri spin_unlock_bh(&wil->eap_lock);
167842fe1e51SAhmad Masri
1679e00243faSLior David rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
1680230d8442SVladimir Kondratiev params->key, key_usage);
1681e41ab937SDedy Lansky if (!rc && !IS_ERR(cs)) {
1682e41ab937SDedy Lansky /* update local storage used for AP recovery */
1683e41ab937SDedy Lansky if (key_usage == WMI_KEY_USE_TX_GROUP && params->key &&
1684e41ab937SDedy Lansky params->key_len <= WMI_MAX_KEY_LEN) {
1685e41ab937SDedy Lansky vif->gtk_index = key_index;
1686e41ab937SDedy Lansky memcpy(vif->gtk, params->key, params->key_len);
1687e41ab937SDedy Lansky vif->gtk_len = params->key_len;
1688e41ab937SDedy Lansky }
1689b9010f10SAhmad Masri /* in FT set crypto will take place upon receiving
1690b9010f10SAhmad Masri * WMI_RING_EN_EVENTID event
1691b9010f10SAhmad Masri */
169274b6ac58SMaya Erez wil_set_crypto_rx(key_index, key_usage, cs, params);
1693e41ab937SDedy Lansky }
169458527421SVladimir Kondratiev
169558527421SVladimir Kondratiev return rc;
16962be7d22fSVladimir Kondratiev }
16972be7d22fSVladimir Kondratiev
wil_cfg80211_del_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_index,bool pairwise,const u8 * mac_addr)16982be7d22fSVladimir Kondratiev static int wil_cfg80211_del_key(struct wiphy *wiphy,
1699*e7a7b84eSVeerendranath Jakkam struct net_device *ndev, int link_id,
17002be7d22fSVladimir Kondratiev u8 key_index, bool pairwise,
17012be7d22fSVladimir Kondratiev const u8 *mac_addr)
17022be7d22fSVladimir Kondratiev {
1703e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
17042be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1705e00243faSLior David struct wireless_dev *wdev = vif_to_wdev(vif);
1706e00243faSLior David enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
1707e00243faSLior David struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
1708e00243faSLior David key_usage,
170958527421SVladimir Kondratiev mac_addr);
17102be7d22fSVladimir Kondratiev
1711af3db60aSLazar Alexei wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
171258527421SVladimir Kondratiev key_usage_str[key_usage], key_index);
171358527421SVladimir Kondratiev
171474b6ac58SMaya Erez if (IS_ERR(cs))
1715af3db60aSLazar Alexei wil_info(wil, "Not connected, %pM %s[%d]\n",
171658527421SVladimir Kondratiev mac_addr, key_usage_str[key_usage], key_index);
171758527421SVladimir Kondratiev
171874b6ac58SMaya Erez if (!IS_ERR_OR_NULL(cs))
171974b6ac58SMaya Erez wil_del_rx_key(key_index, key_usage, cs);
1720db8adcbfSVladimir Kondratiev
1721e00243faSLior David return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage);
17222be7d22fSVladimir Kondratiev }
17232be7d22fSVladimir Kondratiev
17242be7d22fSVladimir Kondratiev /* Need to be present or wiphy_new() will WARN */
wil_cfg80211_set_default_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_index,bool unicast,bool multicast)17252be7d22fSVladimir Kondratiev static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
1726*e7a7b84eSVeerendranath Jakkam struct net_device *ndev, int link_id,
17272be7d22fSVladimir Kondratiev u8 key_index, bool unicast,
17282be7d22fSVladimir Kondratiev bool multicast)
17292be7d22fSVladimir Kondratiev {
1730280ab987SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1731280ab987SLior David
1732af3db60aSLazar Alexei wil_dbg_misc(wil, "set_default_key: entered\n");
17332be7d22fSVladimir Kondratiev return 0;
17342be7d22fSVladimir Kondratiev }
17352be7d22fSVladimir Kondratiev
wil_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,struct ieee80211_channel * chan,unsigned int duration,u64 * cookie)17361647f12fSVladimir Kondratiev static int wil_remain_on_channel(struct wiphy *wiphy,
17371647f12fSVladimir Kondratiev struct wireless_dev *wdev,
17381647f12fSVladimir Kondratiev struct ieee80211_channel *chan,
17391647f12fSVladimir Kondratiev unsigned int duration,
17401647f12fSVladimir Kondratiev u64 *cookie)
17411647f12fSVladimir Kondratiev {
17421647f12fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
17431647f12fSVladimir Kondratiev int rc;
17441647f12fSVladimir Kondratiev
1745af3db60aSLazar Alexei wil_dbg_misc(wil,
1746af3db60aSLazar Alexei "remain_on_channel: center_freq=%d, duration=%d iftype=%d\n",
1747af3db60aSLazar Alexei chan->center_freq, duration, wdev->iftype);
17481647f12fSVladimir Kondratiev
1749bb6743f7SLior David rc = wil_p2p_listen(wil, wdev, duration, chan, cookie);
17501647f12fSVladimir Kondratiev return rc;
17511647f12fSVladimir Kondratiev }
17521647f12fSVladimir Kondratiev
wil_cancel_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,u64 cookie)17531647f12fSVladimir Kondratiev static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
17541647f12fSVladimir Kondratiev struct wireless_dev *wdev,
17551647f12fSVladimir Kondratiev u64 cookie)
17561647f12fSVladimir Kondratiev {
17571647f12fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1758e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
17591647f12fSVladimir Kondratiev
1760af3db60aSLazar Alexei wil_dbg_misc(wil, "cancel_remain_on_channel\n");
17611647f12fSVladimir Kondratiev
1762e00243faSLior David return wil_p2p_cancel_listen(vif, cookie);
17631647f12fSVladimir Kondratiev }
17641647f12fSVladimir Kondratiev
1765691c7a4dSLee Jones /*
1766c100c883SLior David * find a specific IE in a list of IEs
1767c100c883SLior David * return a pointer to the beginning of IE in the list
1768c100c883SLior David * or NULL if not found
1769c100c883SLior David */
_wil_cfg80211_find_ie(const u8 * ies,u16 ies_len,const u8 * ie,u16 ie_len)1770c100c883SLior David static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie,
1771c100c883SLior David u16 ie_len)
1772c100c883SLior David {
1773c100c883SLior David struct ieee80211_vendor_ie *vie;
1774c100c883SLior David u32 oui;
1775c100c883SLior David
1776c100c883SLior David /* IE tag at offset 0, length at offset 1 */
1777c100c883SLior David if (ie_len < 2 || 2 + ie[1] > ie_len)
1778c100c883SLior David return NULL;
1779c100c883SLior David
1780c100c883SLior David if (ie[0] != WLAN_EID_VENDOR_SPECIFIC)
1781c100c883SLior David return cfg80211_find_ie(ie[0], ies, ies_len);
1782c100c883SLior David
1783c100c883SLior David /* make sure there is room for 3 bytes OUI + 1 byte OUI type */
1784c100c883SLior David if (ie[1] < 4)
1785c100c883SLior David return NULL;
1786c100c883SLior David vie = (struct ieee80211_vendor_ie *)ie;
1787c100c883SLior David oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2];
1788c100c883SLior David return cfg80211_find_vendor_ie(oui, vie->oui_type, ies,
1789c100c883SLior David ies_len);
1790c100c883SLior David }
1791c100c883SLior David
1792691c7a4dSLee Jones /*
1793c100c883SLior David * merge the IEs in two lists into a single list.
1794c100c883SLior David * do not include IEs from the second list which exist in the first list.
1795c100c883SLior David * add only vendor specific IEs from second list to keep
1796c100c883SLior David * the merged list sorted (since vendor-specific IE has the
1797c100c883SLior David * highest tag number)
1798c100c883SLior David * caller must free the allocated memory for merged IEs
1799c100c883SLior David */
_wil_cfg80211_merge_extra_ies(const u8 * ies1,u16 ies1_len,const u8 * ies2,u16 ies2_len,u8 ** merged_ies,u16 * merged_len)1800c100c883SLior David static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len,
1801c100c883SLior David const u8 *ies2, u16 ies2_len,
1802c100c883SLior David u8 **merged_ies, u16 *merged_len)
1803c100c883SLior David {
1804c100c883SLior David u8 *buf, *dpos;
1805c100c883SLior David const u8 *spos;
1806c100c883SLior David
1807de77a53cSAlexei Avshalom Lazar if (!ies1)
1808de77a53cSAlexei Avshalom Lazar ies1_len = 0;
1809de77a53cSAlexei Avshalom Lazar
1810de77a53cSAlexei Avshalom Lazar if (!ies2)
1811de77a53cSAlexei Avshalom Lazar ies2_len = 0;
1812de77a53cSAlexei Avshalom Lazar
1813c100c883SLior David if (ies1_len == 0 && ies2_len == 0) {
1814c100c883SLior David *merged_ies = NULL;
1815c100c883SLior David *merged_len = 0;
1816c100c883SLior David return 0;
1817c100c883SLior David }
1818c100c883SLior David
1819c100c883SLior David buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL);
1820c100c883SLior David if (!buf)
1821c100c883SLior David return -ENOMEM;
1822de77a53cSAlexei Avshalom Lazar if (ies1)
1823c100c883SLior David memcpy(buf, ies1, ies1_len);
1824c100c883SLior David dpos = buf + ies1_len;
1825c100c883SLior David spos = ies2;
1826de77a53cSAlexei Avshalom Lazar while (spos && (spos + 1 < ies2 + ies2_len)) {
1827c100c883SLior David /* IE tag at offset 0, length at offset 1 */
1828c100c883SLior David u16 ielen = 2 + spos[1];
1829c100c883SLior David
1830c100c883SLior David if (spos + ielen > ies2 + ies2_len)
1831c100c883SLior David break;
1832c100c883SLior David if (spos[0] == WLAN_EID_VENDOR_SPECIFIC &&
1833de77a53cSAlexei Avshalom Lazar (!ies1 || !_wil_cfg80211_find_ie(ies1, ies1_len,
1834de77a53cSAlexei Avshalom Lazar spos, ielen))) {
1835c100c883SLior David memcpy(dpos, spos, ielen);
1836c100c883SLior David dpos += ielen;
1837c100c883SLior David }
1838c100c883SLior David spos += ielen;
1839c100c883SLior David }
1840c100c883SLior David
1841c100c883SLior David *merged_ies = buf;
1842c100c883SLior David *merged_len = dpos - buf;
1843c100c883SLior David return 0;
1844c100c883SLior David }
1845c100c883SLior David
wil_print_bcon_data(struct cfg80211_beacon_data * b)1846ca959773SVladimir Kondratiev static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
1847ca959773SVladimir Kondratiev {
18485eb443e9SDedy Lansky wil_hex_dump_misc("head ", DUMP_PREFIX_OFFSET, 16, 1,
18495eb443e9SDedy Lansky b->head, b->head_len, true);
18505eb443e9SDedy Lansky wil_hex_dump_misc("tail ", DUMP_PREFIX_OFFSET, 16, 1,
18515eb443e9SDedy Lansky b->tail, b->tail_len, true);
18525eb443e9SDedy Lansky wil_hex_dump_misc("BCON IE ", DUMP_PREFIX_OFFSET, 16, 1,
18535eb443e9SDedy Lansky b->beacon_ies, b->beacon_ies_len, true);
18545eb443e9SDedy Lansky wil_hex_dump_misc("PROBE ", DUMP_PREFIX_OFFSET, 16, 1,
18555eb443e9SDedy Lansky b->probe_resp, b->probe_resp_len, true);
18565eb443e9SDedy Lansky wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1,
18575eb443e9SDedy Lansky b->proberesp_ies, b->proberesp_ies_len, true);
18585eb443e9SDedy Lansky wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1,
18595eb443e9SDedy Lansky b->assocresp_ies, b->assocresp_ies_len, true);
1860ca959773SVladimir Kondratiev }
1861ca959773SVladimir Kondratiev
186233190ebfSVladimir Kondratiev /* internal functions for device reset and starting AP */
1863b9010f10SAhmad Masri static u8 *
_wil_cfg80211_get_proberesp_ies(const u8 * proberesp,u16 proberesp_len,u16 * ies_len)1864b9010f10SAhmad Masri _wil_cfg80211_get_proberesp_ies(const u8 *proberesp, u16 proberesp_len,
1865b9010f10SAhmad Masri u16 *ies_len)
1866b9010f10SAhmad Masri {
1867b9010f10SAhmad Masri u8 *ies = NULL;
1868b9010f10SAhmad Masri
1869b9010f10SAhmad Masri if (proberesp) {
1870b9010f10SAhmad Masri struct ieee80211_mgmt *f =
1871b9010f10SAhmad Masri (struct ieee80211_mgmt *)proberesp;
1872b9010f10SAhmad Masri size_t hlen = offsetof(struct ieee80211_mgmt,
1873b9010f10SAhmad Masri u.probe_resp.variable);
1874b9010f10SAhmad Masri
1875b9010f10SAhmad Masri ies = f->u.probe_resp.variable;
1876b9010f10SAhmad Masri if (ies_len)
1877b9010f10SAhmad Masri *ies_len = proberesp_len - hlen;
1878b9010f10SAhmad Masri }
1879b9010f10SAhmad Masri
1880b9010f10SAhmad Masri return ies;
1881b9010f10SAhmad Masri }
1882b9010f10SAhmad Masri
_wil_cfg80211_set_ies(struct wil6210_vif * vif,struct cfg80211_beacon_data * bcon)1883e00243faSLior David static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
1884cab5abbfSVladimir Kondratiev struct cfg80211_beacon_data *bcon)
188533190ebfSVladimir Kondratiev {
188633190ebfSVladimir Kondratiev int rc;
1887c100c883SLior David u16 len = 0, proberesp_len = 0;
1888b9010f10SAhmad Masri u8 *ies = NULL, *proberesp;
188933190ebfSVladimir Kondratiev
1890e41ab937SDedy Lansky /* update local storage used for AP recovery */
1891e41ab937SDedy Lansky wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, bcon->probe_resp,
1892e41ab937SDedy Lansky bcon->probe_resp_len);
1893e41ab937SDedy Lansky wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len,
1894e41ab937SDedy Lansky bcon->proberesp_ies, bcon->proberesp_ies_len);
1895e41ab937SDedy Lansky wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len,
1896e41ab937SDedy Lansky bcon->assocresp_ies, bcon->assocresp_ies_len);
1897e41ab937SDedy Lansky
1898b9010f10SAhmad Masri proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
1899b9010f10SAhmad Masri bcon->probe_resp_len,
1900b9010f10SAhmad Masri &proberesp_len);
1901c100c883SLior David rc = _wil_cfg80211_merge_extra_ies(proberesp,
1902c100c883SLior David proberesp_len,
1903c100c883SLior David bcon->proberesp_ies,
1904c100c883SLior David bcon->proberesp_ies_len,
1905c100c883SLior David &ies, &len);
1906c100c883SLior David
19075421bf0cSVladimir Kondratiev if (rc)
1908c100c883SLior David goto out;
190933190ebfSVladimir Kondratiev
1910e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies);
1911c100c883SLior David if (rc)
1912c100c883SLior David goto out;
1913c100c883SLior David
1914c100c883SLior David if (bcon->assocresp_ies)
1915e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP,
1916c100c883SLior David bcon->assocresp_ies_len, bcon->assocresp_ies);
1917c100c883SLior David else
1918e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies);
1919cab5abbfSVladimir Kondratiev #if 0 /* to use beacon IE's, remove this #if 0 */
19205421bf0cSVladimir Kondratiev if (rc)
1921c100c883SLior David goto out;
19225421bf0cSVladimir Kondratiev
1923e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_BEACON,
1924e00243faSLior David bcon->tail_len, bcon->tail);
1925cab5abbfSVladimir Kondratiev #endif
1926c100c883SLior David out:
1927c100c883SLior David kfree(ies);
19285421bf0cSVladimir Kondratiev return rc;
192933190ebfSVladimir Kondratiev }
193033190ebfSVladimir Kondratiev
_wil_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,const u8 * ssid,size_t ssid_len,u32 privacy,int bi,u8 chan,u8 wmi_edmg_channel,struct cfg80211_beacon_data * bcon,u8 hidden_ssid,u32 pbss)193133190ebfSVladimir Kondratiev static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
193233190ebfSVladimir Kondratiev struct net_device *ndev,
193333190ebfSVladimir Kondratiev const u8 *ssid, size_t ssid_len, u32 privacy,
19349abe3e30SAlexei Avshalom Lazar int bi, u8 chan, u8 wmi_edmg_channel,
1935cab5abbfSVladimir Kondratiev struct cfg80211_beacon_data *bcon,
1936eabb03b4SLior David u8 hidden_ssid, u32 pbss)
193733190ebfSVladimir Kondratiev {
193833190ebfSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1939e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
194033190ebfSVladimir Kondratiev int rc;
194133190ebfSVladimir Kondratiev struct wireless_dev *wdev = ndev->ieee80211_ptr;
194233190ebfSVladimir Kondratiev u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
1943b4944f2cSLior David u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
1944b9010f10SAhmad Masri u16 proberesp_len = 0;
1945b9010f10SAhmad Masri u8 *proberesp;
1946b9010f10SAhmad Masri bool ft = false;
194733190ebfSVladimir Kondratiev
1948eabb03b4SLior David if (pbss)
1949eabb03b4SLior David wmi_nettype = WMI_NETTYPE_P2P;
1950eabb03b4SLior David
1951e00243faSLior David wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go);
1952b4944f2cSLior David if (is_go && !pbss) {
1953af3db60aSLazar Alexei wil_err(wil, "P2P GO must be in PBSS\n");
1954b4944f2cSLior David return -ENOTSUPP;
1955b4944f2cSLior David }
1956b4944f2cSLior David
195733190ebfSVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle);
195833190ebfSVladimir Kondratiev
1959b9010f10SAhmad Masri proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
1960b9010f10SAhmad Masri bcon->probe_resp_len,
1961b9010f10SAhmad Masri &proberesp_len);
1962b9010f10SAhmad Masri /* check that the probe response IEs has a MDE */
1963b9010f10SAhmad Masri if ((proberesp && proberesp_len > 0 &&
1964b9010f10SAhmad Masri cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN,
1965b9010f10SAhmad Masri proberesp,
1966b9010f10SAhmad Masri proberesp_len)))
1967b9010f10SAhmad Masri ft = true;
1968b9010f10SAhmad Masri
1969b9010f10SAhmad Masri if (ft) {
1970b9010f10SAhmad Masri if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING,
1971b9010f10SAhmad Masri wil->fw_capabilities)) {
1972b9010f10SAhmad Masri wil_err(wil, "FW does not support FT roaming\n");
1973b9010f10SAhmad Masri return -ENOTSUPP;
1974b9010f10SAhmad Masri }
1975b9010f10SAhmad Masri set_bit(wil_vif_ft_roam, vif->status);
1976b9010f10SAhmad Masri }
1977b9010f10SAhmad Masri
197833190ebfSVladimir Kondratiev mutex_lock(&wil->mutex);
197933190ebfSVladimir Kondratiev
19803ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
198133190ebfSVladimir Kondratiev __wil_down(wil);
198233190ebfSVladimir Kondratiev rc = __wil_up(wil);
198333190ebfSVladimir Kondratiev if (rc)
198433190ebfSVladimir Kondratiev goto out;
19853ada9314SLior David }
198633190ebfSVladimir Kondratiev
1987e00243faSLior David rc = wmi_set_ssid(vif, ssid_len, ssid);
198833190ebfSVladimir Kondratiev if (rc)
198933190ebfSVladimir Kondratiev goto out;
199033190ebfSVladimir Kondratiev
1991e00243faSLior David rc = _wil_cfg80211_set_ies(vif, bcon);
199233190ebfSVladimir Kondratiev if (rc)
199333190ebfSVladimir Kondratiev goto out;
199433190ebfSVladimir Kondratiev
1995e00243faSLior David vif->privacy = privacy;
1996e00243faSLior David vif->channel = chan;
19979abe3e30SAlexei Avshalom Lazar vif->wmi_edmg_channel = wmi_edmg_channel;
1998e00243faSLior David vif->hidden_ssid = hidden_ssid;
1999e00243faSLior David vif->pbss = pbss;
2000e41ab937SDedy Lansky vif->bi = bi;
2001e41ab937SDedy Lansky memcpy(vif->ssid, ssid, ssid_len);
2002e41ab937SDedy Lansky vif->ssid_len = ssid_len;
200333190ebfSVladimir Kondratiev
200433190ebfSVladimir Kondratiev netif_carrier_on(ndev);
20053ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, false, true))
20069953a782SLior David wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
200733190ebfSVladimir Kondratiev
20089abe3e30SAlexei Avshalom Lazar rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, wmi_edmg_channel,
20099abe3e30SAlexei Avshalom Lazar hidden_ssid, is_go);
201033190ebfSVladimir Kondratiev if (rc)
201133190ebfSVladimir Kondratiev goto err_pcp_start;
201233190ebfSVladimir Kondratiev
2013e00243faSLior David rc = wil_bcast_init(vif);
201433190ebfSVladimir Kondratiev if (rc)
201533190ebfSVladimir Kondratiev goto err_bcast;
201633190ebfSVladimir Kondratiev
201733190ebfSVladimir Kondratiev goto out; /* success */
201833190ebfSVladimir Kondratiev
201933190ebfSVladimir Kondratiev err_bcast:
2020e00243faSLior David wmi_pcp_stop(vif);
202133190ebfSVladimir Kondratiev err_pcp_start:
202233190ebfSVladimir Kondratiev netif_carrier_off(ndev);
20233ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, false, true))
20249953a782SLior David wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
202533190ebfSVladimir Kondratiev out:
202633190ebfSVladimir Kondratiev mutex_unlock(&wil->mutex);
202733190ebfSVladimir Kondratiev return rc;
202833190ebfSVladimir Kondratiev }
202933190ebfSVladimir Kondratiev
wil_cfg80211_ap_recovery(struct wil6210_priv * wil)2030e41ab937SDedy Lansky void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
2031e41ab937SDedy Lansky {
2032e41ab937SDedy Lansky int rc, i;
2033e41ab937SDedy Lansky struct wiphy *wiphy = wil_to_wiphy(wil);
2034e41ab937SDedy Lansky
2035e4a29bddSAhmad Masri for (i = 0; i < GET_MAX_VIFS(wil); i++) {
2036e41ab937SDedy Lansky struct wil6210_vif *vif = wil->vifs[i];
2037e41ab937SDedy Lansky struct net_device *ndev;
2038e41ab937SDedy Lansky struct cfg80211_beacon_data bcon = {};
2039e41ab937SDedy Lansky struct key_params key_params = {};
2040e41ab937SDedy Lansky
2041e41ab937SDedy Lansky if (!vif || vif->ssid_len == 0)
2042e41ab937SDedy Lansky continue;
2043e41ab937SDedy Lansky
2044e41ab937SDedy Lansky ndev = vif_to_ndev(vif);
2045e41ab937SDedy Lansky bcon.proberesp_ies = vif->proberesp_ies;
2046e41ab937SDedy Lansky bcon.assocresp_ies = vif->assocresp_ies;
2047e41ab937SDedy Lansky bcon.probe_resp = vif->proberesp;
2048e41ab937SDedy Lansky bcon.proberesp_ies_len = vif->proberesp_ies_len;
2049e41ab937SDedy Lansky bcon.assocresp_ies_len = vif->assocresp_ies_len;
2050e41ab937SDedy Lansky bcon.probe_resp_len = vif->proberesp_len;
2051e41ab937SDedy Lansky
2052e41ab937SDedy Lansky wil_info(wil,
2053e41ab937SDedy Lansky "AP (vif %d) recovery: privacy %d, bi %d, channel %d, hidden %d, pbss %d\n",
2054e41ab937SDedy Lansky i, vif->privacy, vif->bi, vif->channel,
2055e41ab937SDedy Lansky vif->hidden_ssid, vif->pbss);
2056e41ab937SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2057e41ab937SDedy Lansky vif->ssid, vif->ssid_len, true);
2058e41ab937SDedy Lansky rc = _wil_cfg80211_start_ap(wiphy, ndev,
2059e41ab937SDedy Lansky vif->ssid, vif->ssid_len,
2060e41ab937SDedy Lansky vif->privacy, vif->bi,
20619abe3e30SAlexei Avshalom Lazar vif->channel,
20629abe3e30SAlexei Avshalom Lazar vif->wmi_edmg_channel, &bcon,
2063e41ab937SDedy Lansky vif->hidden_ssid, vif->pbss);
2064e41ab937SDedy Lansky if (rc) {
2065e41ab937SDedy Lansky wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
2066e41ab937SDedy Lansky continue;
2067e41ab937SDedy Lansky }
2068e41ab937SDedy Lansky
2069e41ab937SDedy Lansky if (!vif->privacy || vif->gtk_len == 0)
2070e41ab937SDedy Lansky continue;
2071e41ab937SDedy Lansky
2072e41ab937SDedy Lansky key_params.key = vif->gtk;
2073e41ab937SDedy Lansky key_params.key_len = vif->gtk_len;
2074e41ab937SDedy Lansky key_params.seq_len = IEEE80211_GCMP_PN_LEN;
2075*e7a7b84eSVeerendranath Jakkam rc = wil_cfg80211_add_key(wiphy, ndev, -1, vif->gtk_index,
2076*e7a7b84eSVeerendranath Jakkam false, NULL, &key_params);
2077e41ab937SDedy Lansky if (rc)
2078e41ab937SDedy Lansky wil_err(wil, "vif %d recovery add key failed (%d)\n",
2079e41ab937SDedy Lansky i, rc);
2080e41ab937SDedy Lansky }
2081e41ab937SDedy Lansky }
2082e41ab937SDedy Lansky
wil_cfg80211_change_beacon(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_beacon_data * bcon)20831bd922fcSVladimir Kondratiev static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
20841bd922fcSVladimir Kondratiev struct net_device *ndev,
20851bd922fcSVladimir Kondratiev struct cfg80211_beacon_data *bcon)
20861bd922fcSVladimir Kondratiev {
20871bd922fcSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2088e41ab937SDedy Lansky struct wireless_dev *wdev = ndev->ieee80211_ptr;
2089e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
20901bd922fcSVladimir Kondratiev int rc;
209133190ebfSVladimir Kondratiev u32 privacy = 0;
20921bd922fcSVladimir Kondratiev
2093e00243faSLior David wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid);
20941e7e5a0dSVladimir Kondratiev wil_print_bcon_data(bcon);
20951e7e5a0dSVladimir Kondratiev
2096c5a157e4SLior David if (bcon->tail &&
2097c5a157e4SLior David cfg80211_find_ie(WLAN_EID_RSN, bcon->tail,
2098c5a157e4SLior David bcon->tail_len))
209933190ebfSVladimir Kondratiev privacy = 1;
21001bd922fcSVladimir Kondratiev
21017b0a0e3cSJohannes Berg memcpy(vif->ssid, wdev->u.ap.ssid, wdev->u.ap.ssid_len);
21027b0a0e3cSJohannes Berg vif->ssid_len = wdev->u.ap.ssid_len;
2103e41ab937SDedy Lansky
210433190ebfSVladimir Kondratiev /* in case privacy has changed, need to restart the AP */
2105e00243faSLior David if (vif->privacy != privacy) {
210633190ebfSVladimir Kondratiev wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
2107e00243faSLior David vif->privacy, privacy);
210833190ebfSVladimir Kondratiev
2109e41ab937SDedy Lansky rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
2110e41ab937SDedy Lansky vif->ssid_len, privacy,
21117b0a0e3cSJohannes Berg wdev->links[0].ap.beacon_interval,
21129abe3e30SAlexei Avshalom Lazar vif->channel,
21139abe3e30SAlexei Avshalom Lazar vif->wmi_edmg_channel, bcon,
2114e00243faSLior David vif->hidden_ssid,
2115e00243faSLior David vif->pbss);
211633190ebfSVladimir Kondratiev } else {
2117e00243faSLior David rc = _wil_cfg80211_set_ies(vif, bcon);
21181bd922fcSVladimir Kondratiev }
21191bd922fcSVladimir Kondratiev
212033190ebfSVladimir Kondratiev return rc;
21211bd922fcSVladimir Kondratiev }
21221bd922fcSVladimir Kondratiev
wil_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ap_settings * info)21232be7d22fSVladimir Kondratiev static int wil_cfg80211_start_ap(struct wiphy *wiphy,
21242be7d22fSVladimir Kondratiev struct net_device *ndev,
21252be7d22fSVladimir Kondratiev struct cfg80211_ap_settings *info)
21262be7d22fSVladimir Kondratiev {
212733190ebfSVladimir Kondratiev int rc;
21282be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
21292be7d22fSVladimir Kondratiev struct ieee80211_channel *channel = info->chandef.chan;
21302be7d22fSVladimir Kondratiev struct cfg80211_beacon_data *bcon = &info->beacon;
2131ca959773SVladimir Kondratiev struct cfg80211_crypto_settings *crypto = &info->crypto;
21329abe3e30SAlexei Avshalom Lazar u8 wmi_edmg_channel;
21338e52fe30SHamad Kadmany u8 hidden_ssid;
21342be7d22fSVladimir Kondratiev
2135af3db60aSLazar Alexei wil_dbg_misc(wil, "start_ap\n");
2136ca959773SVladimir Kondratiev
21379abe3e30SAlexei Avshalom Lazar rc = wil_get_wmi_edmg_channel(wil, info->chandef.edmg.bw_config,
21389abe3e30SAlexei Avshalom Lazar info->chandef.edmg.channels,
21399abe3e30SAlexei Avshalom Lazar &wmi_edmg_channel);
21409abe3e30SAlexei Avshalom Lazar if (rc < 0)
21419abe3e30SAlexei Avshalom Lazar return rc;
21429abe3e30SAlexei Avshalom Lazar
21432be7d22fSVladimir Kondratiev if (!channel) {
21442be7d22fSVladimir Kondratiev wil_err(wil, "AP: No channel???\n");
21452be7d22fSVladimir Kondratiev return -EINVAL;
21462be7d22fSVladimir Kondratiev }
21472be7d22fSVladimir Kondratiev
214833190ebfSVladimir Kondratiev switch (info->hidden_ssid) {
214933190ebfSVladimir Kondratiev case NL80211_HIDDEN_SSID_NOT_IN_USE:
215033190ebfSVladimir Kondratiev hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
215133190ebfSVladimir Kondratiev break;
215233190ebfSVladimir Kondratiev
215333190ebfSVladimir Kondratiev case NL80211_HIDDEN_SSID_ZERO_LEN:
215433190ebfSVladimir Kondratiev hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY;
215533190ebfSVladimir Kondratiev break;
215633190ebfSVladimir Kondratiev
215733190ebfSVladimir Kondratiev case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
215833190ebfSVladimir Kondratiev hidden_ssid = WMI_HIDDEN_SSID_CLEAR;
215933190ebfSVladimir Kondratiev break;
216033190ebfSVladimir Kondratiev
216133190ebfSVladimir Kondratiev default:
216233190ebfSVladimir Kondratiev wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid);
216333190ebfSVladimir Kondratiev return -EOPNOTSUPP;
216433190ebfSVladimir Kondratiev }
21657743882dSVladimir Kondratiev wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
21662be7d22fSVladimir Kondratiev channel->center_freq, info->privacy ? "secure" : "open");
2167ca959773SVladimir Kondratiev wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
2168ca959773SVladimir Kondratiev info->privacy, info->auth_type);
21698e52fe30SHamad Kadmany wil_dbg_misc(wil, "Hidden SSID mode: %d\n",
21708e52fe30SHamad Kadmany info->hidden_ssid);
2171ca959773SVladimir Kondratiev wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
2172ca959773SVladimir Kondratiev info->dtim_period);
2173eabb03b4SLior David wil_dbg_misc(wil, "PBSS %d\n", info->pbss);
21745eb443e9SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
21755eb443e9SDedy Lansky info->ssid, info->ssid_len, true);
2176ca959773SVladimir Kondratiev wil_print_bcon_data(bcon);
2177ca959773SVladimir Kondratiev wil_print_crypto(wil, crypto);
21782be7d22fSVladimir Kondratiev
217933190ebfSVladimir Kondratiev rc = _wil_cfg80211_start_ap(wiphy, ndev,
218033190ebfSVladimir Kondratiev info->ssid, info->ssid_len, info->privacy,
218133190ebfSVladimir Kondratiev info->beacon_interval, channel->hw_value,
21829abe3e30SAlexei Avshalom Lazar wmi_edmg_channel, bcon, hidden_ssid,
21839abe3e30SAlexei Avshalom Lazar info->pbss);
2184c33407a8SVladimir Kondratiev
21852be7d22fSVladimir Kondratiev return rc;
21862be7d22fSVladimir Kondratiev }
21872be7d22fSVladimir Kondratiev
wil_cfg80211_stop_ap(struct wiphy * wiphy,struct net_device * ndev,unsigned int link_id)21882be7d22fSVladimir Kondratiev static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
21897b0a0e3cSJohannes Berg struct net_device *ndev,
21907b0a0e3cSJohannes Berg unsigned int link_id)
21912be7d22fSVladimir Kondratiev {
21922be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2193e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev);
21943ada9314SLior David bool last;
21952be7d22fSVladimir Kondratiev
21963ada9314SLior David wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid);
2197ca959773SVladimir Kondratiev
2198c5e96c91SDedy Lansky netif_carrier_off(ndev);
21993ada9314SLior David last = !wil_has_other_active_ifaces(wil, ndev, false, true);
22003ada9314SLior David if (last) {
22019953a782SLior David wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
2202c33407a8SVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle);
2203646d402dSHamad Kadmany set_bit(wil_status_resetting, wil->status);
22043ada9314SLior David }
2205646d402dSHamad Kadmany
22069c3bde56SVladimir Kondratiev mutex_lock(&wil->mutex);
22079c3bde56SVladimir Kondratiev
2208e00243faSLior David wmi_pcp_stop(vif);
2209b9010f10SAhmad Masri clear_bit(wil_vif_ft_roam, vif->status);
2210e41ab937SDedy Lansky vif->ssid_len = 0;
2211e41ab937SDedy Lansky wil_memdup_ie(&vif->proberesp, &vif->proberesp_len, NULL, 0);
2212e41ab937SDedy Lansky wil_memdup_ie(&vif->proberesp_ies, &vif->proberesp_ies_len, NULL, 0);
2213e41ab937SDedy Lansky wil_memdup_ie(&vif->assocresp_ies, &vif->assocresp_ies_len, NULL, 0);
2214e41ab937SDedy Lansky memset(vif->gtk, 0, WMI_MAX_KEY_LEN);
2215e41ab937SDedy Lansky vif->gtk_len = 0;
22162be7d22fSVladimir Kondratiev
22173ada9314SLior David if (last)
221873d839aeSVladimir Kondratiev __wil_down(wil);
22193ada9314SLior David else
22203ada9314SLior David wil_bcast_fini(vif);
222173d839aeSVladimir Kondratiev
22229c3bde56SVladimir Kondratiev mutex_unlock(&wil->mutex);
222373d839aeSVladimir Kondratiev
222432a20d46SDedy Lansky return 0;
22252be7d22fSVladimir Kondratiev }
22262be7d22fSVladimir Kondratiev
wil_cfg80211_add_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_parameters * params)2227849a564bSDedy Lansky static int wil_cfg80211_add_station(struct wiphy *wiphy,
2228849a564bSDedy Lansky struct net_device *dev,
2229849a564bSDedy Lansky const u8 *mac,
2230849a564bSDedy Lansky struct station_parameters *params)
2231849a564bSDedy Lansky {
2232e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
2233849a564bSDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2234849a564bSDedy Lansky
2235b9010f10SAhmad Masri wil_dbg_misc(wil, "add station %pM aid %d mid %d mask 0x%x set 0x%x\n",
2236b9010f10SAhmad Masri mac, params->aid, vif->mid,
2237b9010f10SAhmad Masri params->sta_flags_mask, params->sta_flags_set);
2238849a564bSDedy Lansky
2239849a564bSDedy Lansky if (!disable_ap_sme) {
2240849a564bSDedy Lansky wil_err(wil, "not supported with AP SME enabled\n");
2241849a564bSDedy Lansky return -EOPNOTSUPP;
2242849a564bSDedy Lansky }
2243849a564bSDedy Lansky
2244849a564bSDedy Lansky if (params->aid > WIL_MAX_DMG_AID) {
2245849a564bSDedy Lansky wil_err(wil, "invalid aid\n");
2246849a564bSDedy Lansky return -EINVAL;
2247849a564bSDedy Lansky }
2248849a564bSDedy Lansky
2249e00243faSLior David return wmi_new_sta(vif, mac, params->aid);
2250849a564bSDedy Lansky }
2251849a564bSDedy Lansky
wil_cfg80211_del_station(struct wiphy * wiphy,struct net_device * dev,struct station_del_parameters * params)22524d55a0a1SVladimir Kondratiev static int wil_cfg80211_del_station(struct wiphy *wiphy,
225389c771e5SJouni Malinen struct net_device *dev,
225489c771e5SJouni Malinen struct station_del_parameters *params)
22554d55a0a1SVladimir Kondratiev {
2256e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
22574d55a0a1SVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2258097638a0SVladimir Kondratiev
2259e00243faSLior David wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n",
2260e00243faSLior David params->mac, params->reason_code, vif->mid);
2261de9084efSVladimir Kondratiev
2262097638a0SVladimir Kondratiev mutex_lock(&wil->mutex);
2263e1b43407SAhmad Masri wil6210_disconnect(vif, params->mac, params->reason_code);
2264097638a0SVladimir Kondratiev mutex_unlock(&wil->mutex);
2265097638a0SVladimir Kondratiev
22664d55a0a1SVladimir Kondratiev return 0;
22674d55a0a1SVladimir Kondratiev }
22684d55a0a1SVladimir Kondratiev
wil_cfg80211_change_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_parameters * params)2269849a564bSDedy Lansky static int wil_cfg80211_change_station(struct wiphy *wiphy,
2270849a564bSDedy Lansky struct net_device *dev,
2271849a564bSDedy Lansky const u8 *mac,
2272849a564bSDedy Lansky struct station_parameters *params)
2273849a564bSDedy Lansky {
2274e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
2275849a564bSDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2276849a564bSDedy Lansky int authorize;
2277849a564bSDedy Lansky int cid, i;
227810590c6aSGidon Studinski struct wil_ring_tx_data *txdata = NULL;
2279849a564bSDedy Lansky
2280e00243faSLior David wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
2281e00243faSLior David mac, params->sta_flags_mask, params->sta_flags_set,
2282e00243faSLior David vif->mid);
2283849a564bSDedy Lansky
2284849a564bSDedy Lansky if (!disable_ap_sme) {
2285849a564bSDedy Lansky wil_dbg_misc(wil, "not supported with AP SME enabled\n");
2286849a564bSDedy Lansky return -EOPNOTSUPP;
2287849a564bSDedy Lansky }
2288849a564bSDedy Lansky
2289849a564bSDedy Lansky if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
2290849a564bSDedy Lansky return 0;
2291849a564bSDedy Lansky
2292e00243faSLior David cid = wil_find_cid(wil, vif->mid, mac);
2293849a564bSDedy Lansky if (cid < 0) {
2294849a564bSDedy Lansky wil_err(wil, "station not found\n");
2295849a564bSDedy Lansky return -ENOLINK;
2296849a564bSDedy Lansky }
2297849a564bSDedy Lansky
229810590c6aSGidon Studinski for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++)
229910590c6aSGidon Studinski if (wil->ring2cid_tid[i][0] == cid) {
230010590c6aSGidon Studinski txdata = &wil->ring_tx_data[i];
2301849a564bSDedy Lansky break;
2302849a564bSDedy Lansky }
2303849a564bSDedy Lansky
2304849a564bSDedy Lansky if (!txdata) {
230510590c6aSGidon Studinski wil_err(wil, "ring data not found\n");
2306849a564bSDedy Lansky return -ENOLINK;
2307849a564bSDedy Lansky }
2308849a564bSDedy Lansky
2309849a564bSDedy Lansky authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED);
2310849a564bSDedy Lansky txdata->dot1x_open = authorize ? 1 : 0;
231110590c6aSGidon Studinski wil_dbg_misc(wil, "cid %d ring %d authorize %d\n", cid, i,
2312849a564bSDedy Lansky txdata->dot1x_open);
2313849a564bSDedy Lansky
2314849a564bSDedy Lansky return 0;
2315849a564bSDedy Lansky }
2316849a564bSDedy Lansky
231740822a90SVladimir Kondratiev /* probe_client handling */
wil_probe_client_handle(struct wil6210_priv * wil,struct wil6210_vif * vif,struct wil_probe_client_req * req)231840822a90SVladimir Kondratiev static void wil_probe_client_handle(struct wil6210_priv *wil,
2319e00243faSLior David struct wil6210_vif *vif,
232040822a90SVladimir Kondratiev struct wil_probe_client_req *req)
232140822a90SVladimir Kondratiev {
2322e00243faSLior David struct net_device *ndev = vif_to_ndev(vif);
232340822a90SVladimir Kondratiev struct wil_sta_info *sta = &wil->sta[req->cid];
232440822a90SVladimir Kondratiev /* assume STA is alive if it is still connected,
232540822a90SVladimir Kondratiev * else FW will disconnect it
232640822a90SVladimir Kondratiev */
232740822a90SVladimir Kondratiev bool alive = (sta->status == wil_sta_connected);
232840822a90SVladimir Kondratiev
2329c4b50cd3SVenkateswara Naralasetty cfg80211_probe_status(ndev, sta->addr, req->cookie, alive,
2330c4b50cd3SVenkateswara Naralasetty 0, false, GFP_KERNEL);
233140822a90SVladimir Kondratiev }
233240822a90SVladimir Kondratiev
next_probe_client(struct wil6210_vif * vif)2333e00243faSLior David static struct list_head *next_probe_client(struct wil6210_vif *vif)
233440822a90SVladimir Kondratiev {
233540822a90SVladimir Kondratiev struct list_head *ret = NULL;
233640822a90SVladimir Kondratiev
2337e00243faSLior David mutex_lock(&vif->probe_client_mutex);
233840822a90SVladimir Kondratiev
2339e00243faSLior David if (!list_empty(&vif->probe_client_pending)) {
2340e00243faSLior David ret = vif->probe_client_pending.next;
234140822a90SVladimir Kondratiev list_del(ret);
234240822a90SVladimir Kondratiev }
234340822a90SVladimir Kondratiev
2344e00243faSLior David mutex_unlock(&vif->probe_client_mutex);
234540822a90SVladimir Kondratiev
234640822a90SVladimir Kondratiev return ret;
234740822a90SVladimir Kondratiev }
234840822a90SVladimir Kondratiev
wil_probe_client_worker(struct work_struct * work)234940822a90SVladimir Kondratiev void wil_probe_client_worker(struct work_struct *work)
235040822a90SVladimir Kondratiev {
2351e00243faSLior David struct wil6210_vif *vif = container_of(work, struct wil6210_vif,
235240822a90SVladimir Kondratiev probe_client_worker);
2353e00243faSLior David struct wil6210_priv *wil = vif_to_wil(vif);
235440822a90SVladimir Kondratiev struct wil_probe_client_req *req;
235540822a90SVladimir Kondratiev struct list_head *lh;
235640822a90SVladimir Kondratiev
2357e00243faSLior David while ((lh = next_probe_client(vif)) != NULL) {
235840822a90SVladimir Kondratiev req = list_entry(lh, struct wil_probe_client_req, list);
235940822a90SVladimir Kondratiev
2360e00243faSLior David wil_probe_client_handle(wil, vif, req);
236140822a90SVladimir Kondratiev kfree(req);
236240822a90SVladimir Kondratiev }
236340822a90SVladimir Kondratiev }
236440822a90SVladimir Kondratiev
wil_probe_client_flush(struct wil6210_vif * vif)2365e00243faSLior David void wil_probe_client_flush(struct wil6210_vif *vif)
236640822a90SVladimir Kondratiev {
236740822a90SVladimir Kondratiev struct wil_probe_client_req *req, *t;
2368e00243faSLior David struct wil6210_priv *wil = vif_to_wil(vif);
236940822a90SVladimir Kondratiev
2370af3db60aSLazar Alexei wil_dbg_misc(wil, "probe_client_flush\n");
237140822a90SVladimir Kondratiev
2372e00243faSLior David mutex_lock(&vif->probe_client_mutex);
237340822a90SVladimir Kondratiev
2374e00243faSLior David list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) {
237540822a90SVladimir Kondratiev list_del(&req->list);
237640822a90SVladimir Kondratiev kfree(req);
237740822a90SVladimir Kondratiev }
237840822a90SVladimir Kondratiev
2379e00243faSLior David mutex_unlock(&vif->probe_client_mutex);
238040822a90SVladimir Kondratiev }
238140822a90SVladimir Kondratiev
wil_cfg80211_probe_client(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,u64 * cookie)238240822a90SVladimir Kondratiev static int wil_cfg80211_probe_client(struct wiphy *wiphy,
238340822a90SVladimir Kondratiev struct net_device *dev,
238440822a90SVladimir Kondratiev const u8 *peer, u64 *cookie)
238540822a90SVladimir Kondratiev {
238640822a90SVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2387e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
238840822a90SVladimir Kondratiev struct wil_probe_client_req *req;
2389e00243faSLior David int cid = wil_find_cid(wil, vif->mid, peer);
239040822a90SVladimir Kondratiev
2391e00243faSLior David wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n",
2392e00243faSLior David peer, cid, vif->mid);
239340822a90SVladimir Kondratiev
239440822a90SVladimir Kondratiev if (cid < 0)
239540822a90SVladimir Kondratiev return -ENOLINK;
239640822a90SVladimir Kondratiev
239740822a90SVladimir Kondratiev req = kzalloc(sizeof(*req), GFP_KERNEL);
239840822a90SVladimir Kondratiev if (!req)
239940822a90SVladimir Kondratiev return -ENOMEM;
240040822a90SVladimir Kondratiev
240140822a90SVladimir Kondratiev req->cid = cid;
240240822a90SVladimir Kondratiev req->cookie = cid;
240340822a90SVladimir Kondratiev
2404e00243faSLior David mutex_lock(&vif->probe_client_mutex);
2405e00243faSLior David list_add_tail(&req->list, &vif->probe_client_pending);
2406e00243faSLior David mutex_unlock(&vif->probe_client_mutex);
240740822a90SVladimir Kondratiev
240840822a90SVladimir Kondratiev *cookie = req->cookie;
2409e00243faSLior David queue_work(wil->wq_service, &vif->probe_client_worker);
241040822a90SVladimir Kondratiev return 0;
241140822a90SVladimir Kondratiev }
241240822a90SVladimir Kondratiev
wil_cfg80211_change_bss(struct wiphy * wiphy,struct net_device * dev,struct bss_parameters * params)241302beaf1aSVladimir Kondratiev static int wil_cfg80211_change_bss(struct wiphy *wiphy,
241402beaf1aSVladimir Kondratiev struct net_device *dev,
241502beaf1aSVladimir Kondratiev struct bss_parameters *params)
241602beaf1aSVladimir Kondratiev {
241702beaf1aSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2418e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
241902beaf1aSVladimir Kondratiev
242002beaf1aSVladimir Kondratiev if (params->ap_isolate >= 0) {
2421e00243faSLior David wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n",
2422e00243faSLior David vif->mid, vif->ap_isolate, params->ap_isolate);
2423e00243faSLior David vif->ap_isolate = params->ap_isolate;
242402beaf1aSVladimir Kondratiev }
242502beaf1aSVladimir Kondratiev
242602beaf1aSVladimir Kondratiev return 0;
242702beaf1aSVladimir Kondratiev }
242802beaf1aSVladimir Kondratiev
wil_cfg80211_set_power_mgmt(struct wiphy * wiphy,struct net_device * dev,bool enabled,int timeout)24292c207eb8SMaya Erez static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
24302c207eb8SMaya Erez struct net_device *dev,
24312c207eb8SMaya Erez bool enabled, int timeout)
24322c207eb8SMaya Erez {
24332c207eb8SMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy);
24342c207eb8SMaya Erez enum wmi_ps_profile_type ps_profile;
24352c207eb8SMaya Erez
24362c207eb8SMaya Erez wil_dbg_misc(wil, "enabled=%d, timeout=%d\n",
24372c207eb8SMaya Erez enabled, timeout);
24382c207eb8SMaya Erez
24392c207eb8SMaya Erez if (enabled)
24402c207eb8SMaya Erez ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;
24412c207eb8SMaya Erez else
24422c207eb8SMaya Erez ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED;
24432c207eb8SMaya Erez
24448b068c03SLazar Alexei return wil_ps_update(wil, ps_profile);
24452c207eb8SMaya Erez }
24462c207eb8SMaya Erez
wil_cfg80211_suspend(struct wiphy * wiphy,struct cfg80211_wowlan * wow)2447fe9ee51eSMaya Erez static int wil_cfg80211_suspend(struct wiphy *wiphy,
2448fe9ee51eSMaya Erez struct cfg80211_wowlan *wow)
2449fe9ee51eSMaya Erez {
2450fe9ee51eSMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2451fe9ee51eSMaya Erez int rc;
2452fe9ee51eSMaya Erez
2453fe9ee51eSMaya Erez /* Setting the wakeup trigger based on wow is TBD */
2454fe9ee51eSMaya Erez
2455fe9ee51eSMaya Erez if (test_bit(wil_status_suspended, wil->status)) {
2456fe9ee51eSMaya Erez wil_dbg_pm(wil, "trying to suspend while suspended\n");
2457fe9ee51eSMaya Erez return 0;
2458fe9ee51eSMaya Erez }
2459fe9ee51eSMaya Erez
2460fe9ee51eSMaya Erez rc = wil_can_suspend(wil, false);
2461fe9ee51eSMaya Erez if (rc)
2462fe9ee51eSMaya Erez goto out;
2463fe9ee51eSMaya Erez
2464fe9ee51eSMaya Erez wil_dbg_pm(wil, "suspending\n");
2465fe9ee51eSMaya Erez
2466144a12a6SHamad Kadmany mutex_lock(&wil->mutex);
2467404bbb3cSLior David mutex_lock(&wil->vif_mutex);
2468144a12a6SHamad Kadmany wil_p2p_stop_radio_operations(wil);
24695bd60982SLior David wil_abort_scan_all_vifs(wil, true);
2470404bbb3cSLior David mutex_unlock(&wil->vif_mutex);
2471144a12a6SHamad Kadmany mutex_unlock(&wil->mutex);
2472fe9ee51eSMaya Erez
2473fe9ee51eSMaya Erez out:
2474fe9ee51eSMaya Erez return rc;
2475fe9ee51eSMaya Erez }
2476fe9ee51eSMaya Erez
wil_cfg80211_resume(struct wiphy * wiphy)2477fe9ee51eSMaya Erez static int wil_cfg80211_resume(struct wiphy *wiphy)
2478fe9ee51eSMaya Erez {
2479fe9ee51eSMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2480fe9ee51eSMaya Erez
2481fe9ee51eSMaya Erez wil_dbg_pm(wil, "resuming\n");
2482fe9ee51eSMaya Erez
2483fe9ee51eSMaya Erez return 0;
2484fe9ee51eSMaya Erez }
2485fe9ee51eSMaya Erez
2486a5dc6883SDedy Lansky static int
wil_cfg80211_sched_scan_start(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_sched_scan_request * request)2487a5dc6883SDedy Lansky wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
2488a5dc6883SDedy Lansky struct net_device *dev,
2489a5dc6883SDedy Lansky struct cfg80211_sched_scan_request *request)
2490a5dc6883SDedy Lansky {
2491a5dc6883SDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2492e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
2493a5dc6883SDedy Lansky int i, rc;
2494a5dc6883SDedy Lansky
2495e00243faSLior David if (vif->mid != 0)
2496e00243faSLior David return -EOPNOTSUPP;
2497e00243faSLior David
2498a5dc6883SDedy Lansky wil_dbg_misc(wil,
2499a5dc6883SDedy Lansky "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
2500a5dc6883SDedy Lansky request->n_ssids, request->ie_len, request->flags);
2501a5dc6883SDedy Lansky for (i = 0; i < request->n_ssids; i++) {
2502a5dc6883SDedy Lansky wil_dbg_misc(wil, "SSID[%d]:", i);
2503a5dc6883SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2504a5dc6883SDedy Lansky request->ssids[i].ssid,
2505a5dc6883SDedy Lansky request->ssids[i].ssid_len, true);
2506a5dc6883SDedy Lansky }
2507a5dc6883SDedy Lansky wil_dbg_misc(wil, "channels:");
2508a5dc6883SDedy Lansky for (i = 0; i < request->n_channels; i++)
2509a5dc6883SDedy Lansky wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value,
2510a5dc6883SDedy Lansky i == request->n_channels - 1 ? "\n" : "");
2511a5dc6883SDedy Lansky wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n",
2512a5dc6883SDedy Lansky request->n_match_sets, request->min_rssi_thold,
2513a5dc6883SDedy Lansky request->delay);
2514a5dc6883SDedy Lansky for (i = 0; i < request->n_match_sets; i++) {
2515a5dc6883SDedy Lansky struct cfg80211_match_set *ms = &request->match_sets[i];
2516a5dc6883SDedy Lansky
2517a5dc6883SDedy Lansky wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n",
2518a5dc6883SDedy Lansky i, ms->rssi_thold);
2519a5dc6883SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
2520a5dc6883SDedy Lansky ms->ssid.ssid,
2521a5dc6883SDedy Lansky ms->ssid.ssid_len, true);
2522a5dc6883SDedy Lansky }
2523a5dc6883SDedy Lansky wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans);
2524a5dc6883SDedy Lansky for (i = 0; i < request->n_scan_plans; i++) {
2525a5dc6883SDedy Lansky struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i];
2526a5dc6883SDedy Lansky
2527a5dc6883SDedy Lansky wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n",
2528a5dc6883SDedy Lansky i, sp->interval, sp->iterations);
2529a5dc6883SDedy Lansky }
2530a5dc6883SDedy Lansky
2531e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
2532e00243faSLior David request->ie_len, request->ie);
2533a5dc6883SDedy Lansky if (rc)
2534a5dc6883SDedy Lansky return rc;
2535a5dc6883SDedy Lansky return wmi_start_sched_scan(wil, request);
2536a5dc6883SDedy Lansky }
2537a5dc6883SDedy Lansky
2538a5dc6883SDedy Lansky static int
wil_cfg80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * dev,u64 reqid)2539a5dc6883SDedy Lansky wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
2540a5dc6883SDedy Lansky u64 reqid)
2541a5dc6883SDedy Lansky {
2542a5dc6883SDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2543e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev);
2544a5dc6883SDedy Lansky int rc;
2545a5dc6883SDedy Lansky
2546e00243faSLior David if (vif->mid != 0)
2547e00243faSLior David return -EOPNOTSUPP;
2548e00243faSLior David
2549a5dc6883SDedy Lansky rc = wmi_stop_sched_scan(wil);
2550a5dc6883SDedy Lansky /* device would return error if it thinks PNO is already stopped.
2551a5dc6883SDedy Lansky * ignore the return code so user space and driver gets back in-sync
2552a5dc6883SDedy Lansky */
2553a5dc6883SDedy Lansky wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc);
2554a5dc6883SDedy Lansky
2555a5dc6883SDedy Lansky return 0;
2556a5dc6883SDedy Lansky }
2557a5dc6883SDedy Lansky
2558b9010f10SAhmad Masri static int
wil_cfg80211_update_ft_ies(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_update_ft_ies_params * ftie)2559b9010f10SAhmad Masri wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
2560b9010f10SAhmad Masri struct cfg80211_update_ft_ies_params *ftie)
2561b9010f10SAhmad Masri {
2562b9010f10SAhmad Masri struct wil6210_priv *wil = wiphy_to_wil(wiphy);
2563b9010f10SAhmad Masri struct wil6210_vif *vif = ndev_to_vif(dev);
2564b9010f10SAhmad Masri struct cfg80211_bss *bss;
2565b9010f10SAhmad Masri struct wmi_ft_reassoc_cmd reassoc;
2566b9010f10SAhmad Masri int rc = 0;
2567b9010f10SAhmad Masri
2568b9010f10SAhmad Masri wil_dbg_misc(wil, "update ft ies, mid=%d\n", vif->mid);
2569b9010f10SAhmad Masri wil_hex_dump_misc("FT IE ", DUMP_PREFIX_OFFSET, 16, 1,
2570b9010f10SAhmad Masri ftie->ie, ftie->ie_len, true);
2571b9010f10SAhmad Masri
2572b9010f10SAhmad Masri if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
2573b9010f10SAhmad Masri wil_err(wil, "FW does not support FT roaming\n");
2574b9010f10SAhmad Masri return -EOPNOTSUPP;
2575b9010f10SAhmad Masri }
2576b9010f10SAhmad Masri
2577b9010f10SAhmad Masri rc = wmi_update_ft_ies(vif, ftie->ie_len, ftie->ie);
2578b9010f10SAhmad Masri if (rc)
2579b9010f10SAhmad Masri return rc;
2580b9010f10SAhmad Masri
2581b9010f10SAhmad Masri if (!test_bit(wil_vif_ft_roam, vif->status))
2582b9010f10SAhmad Masri /* vif is not roaming */
2583b9010f10SAhmad Masri return 0;
2584b9010f10SAhmad Masri
2585b9010f10SAhmad Masri /* wil_vif_ft_roam is set. wil_cfg80211_update_ft_ies is used as
2586b9010f10SAhmad Masri * a trigger for reassoc
2587b9010f10SAhmad Masri */
2588b9010f10SAhmad Masri
2589b9010f10SAhmad Masri bss = vif->bss;
2590b9010f10SAhmad Masri if (!bss) {
2591b9010f10SAhmad Masri wil_err(wil, "FT: bss is NULL\n");
2592b9010f10SAhmad Masri return -EINVAL;
2593b9010f10SAhmad Masri }
2594b9010f10SAhmad Masri
2595b9010f10SAhmad Masri memset(&reassoc, 0, sizeof(reassoc));
2596b9010f10SAhmad Masri ether_addr_copy(reassoc.bssid, bss->bssid);
2597b9010f10SAhmad Masri
2598b9010f10SAhmad Masri rc = wmi_send(wil, WMI_FT_REASSOC_CMDID, vif->mid,
2599b9010f10SAhmad Masri &reassoc, sizeof(reassoc));
2600b9010f10SAhmad Masri if (rc)
2601b9010f10SAhmad Masri wil_err(wil, "FT: reassoc failed (%d)\n", rc);
2602b9010f10SAhmad Masri
2603b9010f10SAhmad Masri return rc;
2604b9010f10SAhmad Masri }
2605b9010f10SAhmad Masri
wil_cfg80211_set_multicast_to_unicast(struct wiphy * wiphy,struct net_device * dev,const bool enabled)26065e5f069cSAhmad Masri static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy,
26075e5f069cSAhmad Masri struct net_device *dev,
26085e5f069cSAhmad Masri const bool enabled)
26095e5f069cSAhmad Masri {
26105e5f069cSAhmad Masri struct wil6210_priv *wil = wiphy_to_wil(wiphy);
26115e5f069cSAhmad Masri
26125e5f069cSAhmad Masri if (wil->multicast_to_unicast == enabled)
26135e5f069cSAhmad Masri return 0;
26145e5f069cSAhmad Masri
26155e5f069cSAhmad Masri wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled);
26165e5f069cSAhmad Masri wil->multicast_to_unicast = enabled;
26175e5f069cSAhmad Masri
26185e5f069cSAhmad Masri return 0;
26195e5f069cSAhmad Masri }
26205e5f069cSAhmad Masri
wil_cfg80211_set_cqm_rssi_config(struct wiphy * wiphy,struct net_device * dev,s32 rssi_thold,u32 rssi_hyst)26214315a74aSDedy Lansky static int wil_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
26224315a74aSDedy Lansky struct net_device *dev,
26234315a74aSDedy Lansky s32 rssi_thold, u32 rssi_hyst)
26244315a74aSDedy Lansky {
26254315a74aSDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy);
26264315a74aSDedy Lansky int rc;
26274315a74aSDedy Lansky
26284315a74aSDedy Lansky wil->cqm_rssi_thold = rssi_thold;
26294315a74aSDedy Lansky
26304315a74aSDedy Lansky rc = wmi_set_cqm_rssi_config(wil, rssi_thold, rssi_hyst);
26314315a74aSDedy Lansky if (rc)
26324315a74aSDedy Lansky /* reset stored value upon failure */
26334315a74aSDedy Lansky wil->cqm_rssi_thold = 0;
26344315a74aSDedy Lansky
26354315a74aSDedy Lansky return rc;
26364315a74aSDedy Lansky }
26374315a74aSDedy Lansky
2638b59eb961SBhumika Goyal static const struct cfg80211_ops wil_cfg80211_ops = {
26394332cac1SLior David .add_virtual_intf = wil_cfg80211_add_iface,
26404332cac1SLior David .del_virtual_intf = wil_cfg80211_del_iface,
26412be7d22fSVladimir Kondratiev .scan = wil_cfg80211_scan,
2642035859a5SMaya Erez .abort_scan = wil_cfg80211_abort_scan,
26432be7d22fSVladimir Kondratiev .connect = wil_cfg80211_connect,
26442be7d22fSVladimir Kondratiev .disconnect = wil_cfg80211_disconnect,
26453fea18d0SLior David .set_wiphy_params = wil_cfg80211_set_wiphy_params,
26462be7d22fSVladimir Kondratiev .change_virtual_intf = wil_cfg80211_change_iface,
26472be7d22fSVladimir Kondratiev .get_station = wil_cfg80211_get_station,
2648ef28afdbSVladimir Kondratiev .dump_station = wil_cfg80211_dump_station,
26491647f12fSVladimir Kondratiev .remain_on_channel = wil_remain_on_channel,
26501647f12fSVladimir Kondratiev .cancel_remain_on_channel = wil_cancel_remain_on_channel,
26511647f12fSVladimir Kondratiev .mgmt_tx = wil_cfg80211_mgmt_tx,
26522be7d22fSVladimir Kondratiev .set_monitor_channel = wil_cfg80211_set_channel,
26532be7d22fSVladimir Kondratiev .add_key = wil_cfg80211_add_key,
26542be7d22fSVladimir Kondratiev .del_key = wil_cfg80211_del_key,
26552be7d22fSVladimir Kondratiev .set_default_key = wil_cfg80211_set_default_key,
26562be7d22fSVladimir Kondratiev /* AP mode */
26571bd922fcSVladimir Kondratiev .change_beacon = wil_cfg80211_change_beacon,
26582be7d22fSVladimir Kondratiev .start_ap = wil_cfg80211_start_ap,
26592be7d22fSVladimir Kondratiev .stop_ap = wil_cfg80211_stop_ap,
2660849a564bSDedy Lansky .add_station = wil_cfg80211_add_station,
26614d55a0a1SVladimir Kondratiev .del_station = wil_cfg80211_del_station,
2662849a564bSDedy Lansky .change_station = wil_cfg80211_change_station,
266340822a90SVladimir Kondratiev .probe_client = wil_cfg80211_probe_client,
266402beaf1aSVladimir Kondratiev .change_bss = wil_cfg80211_change_bss,
26654332cac1SLior David /* P2P device */
26664332cac1SLior David .start_p2p_device = wil_cfg80211_start_p2p_device,
26674332cac1SLior David .stop_p2p_device = wil_cfg80211_stop_p2p_device,
26682c207eb8SMaya Erez .set_power_mgmt = wil_cfg80211_set_power_mgmt,
26694315a74aSDedy Lansky .set_cqm_rssi_config = wil_cfg80211_set_cqm_rssi_config,
2670fe9ee51eSMaya Erez .suspend = wil_cfg80211_suspend,
2671fe9ee51eSMaya Erez .resume = wil_cfg80211_resume,
2672a5dc6883SDedy Lansky .sched_scan_start = wil_cfg80211_sched_scan_start,
2673a5dc6883SDedy Lansky .sched_scan_stop = wil_cfg80211_sched_scan_stop,
2674b9010f10SAhmad Masri .update_ft_ies = wil_cfg80211_update_ft_ies,
26755e5f069cSAhmad Masri .set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast,
26762be7d22fSVladimir Kondratiev };
26772be7d22fSVladimir Kondratiev
wil_wiphy_init(struct wiphy * wiphy)26782be7d22fSVladimir Kondratiev static void wil_wiphy_init(struct wiphy *wiphy)
26792be7d22fSVladimir Kondratiev {
26808e52fe30SHamad Kadmany wiphy->max_scan_ssids = 1;
268177c91295SVladimir Kondratiev wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
2682e6d68341SDedy Lansky wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS;
26832be7d22fSVladimir Kondratiev wiphy->max_num_pmkids = 0 /* TODO: */;
26842be7d22fSVladimir Kondratiev wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
26852be7d22fSVladimir Kondratiev BIT(NL80211_IFTYPE_AP) |
2686e6d68341SDedy Lansky BIT(NL80211_IFTYPE_P2P_CLIENT) |
2687e6d68341SDedy Lansky BIT(NL80211_IFTYPE_P2P_GO) |
26884332cac1SLior David BIT(NL80211_IFTYPE_P2P_DEVICE) |
2689e6d68341SDedy Lansky BIT(NL80211_IFTYPE_MONITOR);
2690849a564bSDedy Lansky wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
26912c207eb8SMaya Erez WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
26922c207eb8SMaya Erez WIPHY_FLAG_PS_ON_BY_DEFAULT;
2693849a564bSDedy Lansky if (!disable_ap_sme)
2694849a564bSDedy Lansky wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
26959cf10d62SVladimir Kondratiev dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
26962be7d22fSVladimir Kondratiev __func__, wiphy->flags);
26972be7d22fSVladimir Kondratiev wiphy->probe_resp_offload =
26982be7d22fSVladimir Kondratiev NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
26992be7d22fSVladimir Kondratiev NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
27002be7d22fSVladimir Kondratiev NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
27012be7d22fSVladimir Kondratiev
270257fbcce3SJohannes Berg wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz;
27032be7d22fSVladimir Kondratiev
270430868f5dSDedy Lansky /* may change after reading FW capabilities */
2705b8b33a3aSVladimir Kondratiev wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
27062be7d22fSVladimir Kondratiev
27072be7d22fSVladimir Kondratiev wiphy->cipher_suites = wil_cipher_suites;
27082be7d22fSVladimir Kondratiev wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
27092be7d22fSVladimir Kondratiev wiphy->mgmt_stypes = wil_mgmt_stypes;
2710713c8a29SVladimir Kondratiev wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
27110216a895SLior David
27120216a895SLior David wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
27130216a895SLior David wiphy->vendor_commands = wil_nl80211_vendor_commands;
2714d1fbf075SMaya Erez
2715d1fbf075SMaya Erez #ifdef CONFIG_PM
2716d1fbf075SMaya Erez wiphy->wowlan = &wil_wowlan_support;
2717d1fbf075SMaya Erez #endif
27182be7d22fSVladimir Kondratiev }
27192be7d22fSVladimir Kondratiev
wil_cfg80211_iface_combinations_from_fw(struct wil6210_priv * wil,const struct wil_fw_record_concurrency * conc)27207bfe9e22SLior David int wil_cfg80211_iface_combinations_from_fw(
27217bfe9e22SLior David struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
27227bfe9e22SLior David {
27237bfe9e22SLior David struct wiphy *wiphy = wil_to_wiphy(wil);
27247bfe9e22SLior David u32 total_limits = 0;
27257bfe9e22SLior David u16 n_combos;
27267bfe9e22SLior David const struct wil_fw_concurrency_combo *combo;
27277bfe9e22SLior David const struct wil_fw_concurrency_limit *limit;
27287bfe9e22SLior David struct ieee80211_iface_combination *iface_combinations;
27297bfe9e22SLior David struct ieee80211_iface_limit *iface_limit;
27307bfe9e22SLior David int i, j;
27317bfe9e22SLior David
27327bfe9e22SLior David if (wiphy->iface_combinations) {
27337bfe9e22SLior David wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
27347bfe9e22SLior David return 0;
27357bfe9e22SLior David }
27367bfe9e22SLior David
27377bfe9e22SLior David combo = conc->combos;
27387bfe9e22SLior David n_combos = le16_to_cpu(conc->n_combos);
27397bfe9e22SLior David for (i = 0; i < n_combos; i++) {
27407bfe9e22SLior David total_limits += combo->n_limits;
27417bfe9e22SLior David limit = combo->limits + combo->n_limits;
27427bfe9e22SLior David combo = (struct wil_fw_concurrency_combo *)limit;
27437bfe9e22SLior David }
27447bfe9e22SLior David
27457bfe9e22SLior David iface_combinations =
27467bfe9e22SLior David kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
27477bfe9e22SLior David total_limits * sizeof(struct ieee80211_iface_limit),
27487bfe9e22SLior David GFP_KERNEL);
27497bfe9e22SLior David if (!iface_combinations)
27507bfe9e22SLior David return -ENOMEM;
27517bfe9e22SLior David iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
27527bfe9e22SLior David n_combos);
27537bfe9e22SLior David combo = conc->combos;
27547bfe9e22SLior David for (i = 0; i < n_combos; i++) {
27557bfe9e22SLior David iface_combinations[i].max_interfaces = combo->max_interfaces;
27567bfe9e22SLior David iface_combinations[i].num_different_channels =
27577bfe9e22SLior David combo->n_diff_channels;
27587bfe9e22SLior David iface_combinations[i].beacon_int_infra_match =
27597bfe9e22SLior David combo->same_bi;
27607bfe9e22SLior David iface_combinations[i].n_limits = combo->n_limits;
27617bfe9e22SLior David wil_dbg_misc(wil,
27627bfe9e22SLior David "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
27637bfe9e22SLior David i, iface_combinations[i].max_interfaces,
27647bfe9e22SLior David iface_combinations[i].num_different_channels,
27657bfe9e22SLior David iface_combinations[i].beacon_int_infra_match);
27667bfe9e22SLior David limit = combo->limits;
27677bfe9e22SLior David for (j = 0; j < combo->n_limits; j++) {
27687bfe9e22SLior David iface_limit[j].max = le16_to_cpu(limit[j].max);
27697bfe9e22SLior David iface_limit[j].types = le16_to_cpu(limit[j].types);
27707bfe9e22SLior David wil_dbg_misc(wil,
27717bfe9e22SLior David "limit %d: max %d types 0x%x\n", j,
27727bfe9e22SLior David iface_limit[j].max, iface_limit[j].types);
27737bfe9e22SLior David }
27747bfe9e22SLior David iface_combinations[i].limits = iface_limit;
27757bfe9e22SLior David iface_limit += combo->n_limits;
27767bfe9e22SLior David limit += combo->n_limits;
27777bfe9e22SLior David combo = (struct wil_fw_concurrency_combo *)limit;
27787bfe9e22SLior David }
27797bfe9e22SLior David
27804aebd3bdSLior David wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids);
27814aebd3bdSLior David wil->max_vifs = conc->n_mids + 1; /* including main interface */
27824aebd3bdSLior David if (wil->max_vifs > WIL_MAX_VIFS) {
27834aebd3bdSLior David wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n",
27844aebd3bdSLior David WIL_MAX_VIFS, wil->max_vifs);
27854aebd3bdSLior David wil->max_vifs = WIL_MAX_VIFS;
27864aebd3bdSLior David }
27877bfe9e22SLior David wiphy->n_iface_combinations = n_combos;
27887bfe9e22SLior David wiphy->iface_combinations = iface_combinations;
27897bfe9e22SLior David return 0;
27907bfe9e22SLior David }
27917bfe9e22SLior David
wil_cfg80211_init(struct device * dev)27929f38f286SLior David struct wil6210_priv *wil_cfg80211_init(struct device *dev)
27932be7d22fSVladimir Kondratiev {
27949f38f286SLior David struct wiphy *wiphy;
27959f38f286SLior David struct wil6210_priv *wil;
27969f38f286SLior David struct ieee80211_channel *ch;
27972be7d22fSVladimir Kondratiev
27989cf10d62SVladimir Kondratiev dev_dbg(dev, "%s()\n", __func__);
27999cf10d62SVladimir Kondratiev
28009f38f286SLior David /* Note: the wireless_dev structure is no longer allocated here.
28019f38f286SLior David * Instead, it is allocated as part of the net_device structure
28029f38f286SLior David * for main interface and each VIF.
28039f38f286SLior David */
28049f38f286SLior David wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv));
28059f38f286SLior David if (!wiphy)
28062be7d22fSVladimir Kondratiev return ERR_PTR(-ENOMEM);
28072be7d22fSVladimir Kondratiev
28089f38f286SLior David set_wiphy_dev(wiphy, dev);
28099f38f286SLior David wil_wiphy_init(wiphy);
28109f38f286SLior David
28119f38f286SLior David wil = wiphy_to_wil(wiphy);
28129f38f286SLior David wil->wiphy = wiphy;
28139f38f286SLior David
28149f38f286SLior David /* default monitor channel */
28159f38f286SLior David ch = wiphy->bands[NL80211_BAND_60GHZ]->channels;
28169f38f286SLior David cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
28179f38f286SLior David
28189f38f286SLior David return wil;
28192be7d22fSVladimir Kondratiev }
28202be7d22fSVladimir Kondratiev
wil_cfg80211_deinit(struct wil6210_priv * wil)28219f38f286SLior David void wil_cfg80211_deinit(struct wil6210_priv *wil)
28222be7d22fSVladimir Kondratiev {
28239f38f286SLior David struct wiphy *wiphy = wil_to_wiphy(wil);
28242be7d22fSVladimir Kondratiev
28259cf10d62SVladimir Kondratiev dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
28269cf10d62SVladimir Kondratiev
28279f38f286SLior David if (!wiphy)
28282be7d22fSVladimir Kondratiev return;
28292be7d22fSVladimir Kondratiev
28307bfe9e22SLior David kfree(wiphy->iface_combinations);
28317bfe9e22SLior David wiphy->iface_combinations = NULL;
28327bfe9e22SLior David
28339f38f286SLior David wiphy_free(wiphy);
28349f38f286SLior David /* do not access wil6210_priv after returning from here */
28352be7d22fSVladimir Kondratiev }
28364332cac1SLior David
wil_p2p_wdev_free(struct wil6210_priv * wil)28374332cac1SLior David void wil_p2p_wdev_free(struct wil6210_priv *wil)
28384332cac1SLior David {
28394332cac1SLior David struct wireless_dev *p2p_wdev;
28404332cac1SLior David
2841404bbb3cSLior David mutex_lock(&wil->vif_mutex);
28424332cac1SLior David p2p_wdev = wil->p2p_wdev;
28434332cac1SLior David wil->p2p_wdev = NULL;
2844e00243faSLior David wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
2845404bbb3cSLior David mutex_unlock(&wil->vif_mutex);
2846d35c2b6fSMaya Erez if (p2p_wdev) {
28474332cac1SLior David cfg80211_unregister_wdev(p2p_wdev);
28485bd60982SLior David kfree(p2p_wdev);
28494332cac1SLior David }
28504332cac1SLior David }
28510216a895SLior David
wil_rf_sector_status_to_rc(u8 status)28520216a895SLior David static int wil_rf_sector_status_to_rc(u8 status)
28530216a895SLior David {
28540216a895SLior David switch (status) {
28550216a895SLior David case WMI_RF_SECTOR_STATUS_SUCCESS:
28560216a895SLior David return 0;
28570216a895SLior David case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR:
28580216a895SLior David return -EINVAL;
28590216a895SLior David case WMI_RF_SECTOR_STATUS_BUSY_ERROR:
28600216a895SLior David return -EAGAIN;
28610216a895SLior David case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR:
28620216a895SLior David return -EOPNOTSUPP;
28630216a895SLior David default:
28640216a895SLior David return -EINVAL;
28650216a895SLior David }
28660216a895SLior David }
28670216a895SLior David
wil_rf_sector_get_cfg(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)28680216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
28690216a895SLior David struct wireless_dev *wdev,
28700216a895SLior David const void *data, int data_len)
28710216a895SLior David {
28720216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev);
2873e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
28740216a895SLior David int rc;
28750216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
28760216a895SLior David u16 sector_index;
28770216a895SLior David u8 sector_type;
28780216a895SLior David u32 rf_modules_vec;
28790216a895SLior David struct wmi_get_rf_sector_params_cmd cmd;
28800216a895SLior David struct {
28810216a895SLior David struct wmi_cmd_hdr wmi;
28820216a895SLior David struct wmi_get_rf_sector_params_done_event evt;
2883807b0860SAlexei Avshalom Lazar } __packed reply = {
2884807b0860SAlexei Avshalom Lazar .evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
2885807b0860SAlexei Avshalom Lazar };
28860216a895SLior David struct sk_buff *msg;
28870216a895SLior David struct nlattr *nl_cfgs, *nl_cfg;
28880216a895SLior David u32 i;
28890216a895SLior David struct wmi_rf_sector_info *si;
28900216a895SLior David
28910216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
28920216a895SLior David return -EOPNOTSUPP;
28930216a895SLior David
28948cb08174SJohannes Berg rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
28958cb08174SJohannes Berg data_len, wil_rf_sector_policy, NULL);
28960216a895SLior David if (rc) {
28970216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n");
28980216a895SLior David return rc;
28990216a895SLior David }
29000216a895SLior David
29010216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
29020216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
29030216a895SLior David !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) {
29040216a895SLior David wil_err(wil, "Invalid rf sector spec\n");
29050216a895SLior David return -EINVAL;
29060216a895SLior David }
29070216a895SLior David
29080216a895SLior David sector_index = nla_get_u16(
29090216a895SLior David tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
29100216a895SLior David if (sector_index >= WIL_MAX_RF_SECTORS) {
29110216a895SLior David wil_err(wil, "Invalid sector index %d\n", sector_index);
29120216a895SLior David return -EINVAL;
29130216a895SLior David }
29140216a895SLior David
29150216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
29160216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
29170216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type);
29180216a895SLior David return -EINVAL;
29190216a895SLior David }
29200216a895SLior David
29210216a895SLior David rf_modules_vec = nla_get_u32(
29220216a895SLior David tb[QCA_ATTR_DMG_RF_MODULE_MASK]);
29230216a895SLior David if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) {
29240216a895SLior David wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec);
29250216a895SLior David return -EINVAL;
29260216a895SLior David }
29270216a895SLior David
29280216a895SLior David cmd.sector_idx = cpu_to_le16(sector_index);
29290216a895SLior David cmd.sector_type = sector_type;
29300216a895SLior David cmd.rf_modules_vec = rf_modules_vec & 0xFF;
2931e00243faSLior David rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid,
2932e00243faSLior David &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
29330216a895SLior David &reply, sizeof(reply),
29340216a895SLior David 500);
29350216a895SLior David if (rc)
29360216a895SLior David return rc;
29370216a895SLior David if (reply.evt.status) {
29380216a895SLior David wil_err(wil, "get rf sector cfg failed with status %d\n",
29390216a895SLior David reply.evt.status);
29400216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status);
29410216a895SLior David }
29420216a895SLior David
29430216a895SLior David msg = cfg80211_vendor_cmd_alloc_reply_skb(
29440216a895SLior David wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
29450216a895SLior David if (!msg)
29460216a895SLior David return -ENOMEM;
29470216a895SLior David
29480216a895SLior David if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
29490216a895SLior David le64_to_cpu(reply.evt.tsf),
29500216a895SLior David QCA_ATTR_PAD))
29510216a895SLior David goto nla_put_failure;
29520216a895SLior David
2953ae0be8deSMichal Kubecek nl_cfgs = nla_nest_start_noflag(msg, QCA_ATTR_DMG_RF_SECTOR_CFG);
29540216a895SLior David if (!nl_cfgs)
29550216a895SLior David goto nla_put_failure;
29560216a895SLior David for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) {
29570216a895SLior David if (!(rf_modules_vec & BIT(i)))
29580216a895SLior David continue;
2959ae0be8deSMichal Kubecek nl_cfg = nla_nest_start_noflag(msg, i);
29600216a895SLior David if (!nl_cfg)
29610216a895SLior David goto nla_put_failure;
29620216a895SLior David si = &reply.evt.sectors_info[i];
29630216a895SLior David if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX,
29640216a895SLior David i) ||
29650216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0,
29660216a895SLior David le32_to_cpu(si->etype0)) ||
29670216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1,
29680216a895SLior David le32_to_cpu(si->etype1)) ||
29690216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2,
29700216a895SLior David le32_to_cpu(si->etype2)) ||
29710216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI,
29720216a895SLior David le32_to_cpu(si->psh_hi)) ||
29730216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO,
29740216a895SLior David le32_to_cpu(si->psh_lo)) ||
29750216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16,
29760216a895SLior David le32_to_cpu(si->dtype_swch_off)))
29770216a895SLior David goto nla_put_failure;
29780216a895SLior David nla_nest_end(msg, nl_cfg);
29790216a895SLior David }
29800216a895SLior David
29810216a895SLior David nla_nest_end(msg, nl_cfgs);
29820216a895SLior David rc = cfg80211_vendor_cmd_reply(msg);
29830216a895SLior David return rc;
29840216a895SLior David nla_put_failure:
29850216a895SLior David kfree_skb(msg);
29860216a895SLior David return -ENOBUFS;
29870216a895SLior David }
29880216a895SLior David
wil_rf_sector_set_cfg(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)29890216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
29900216a895SLior David struct wireless_dev *wdev,
29910216a895SLior David const void *data, int data_len)
29920216a895SLior David {
29930216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev);
2994e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
29950216a895SLior David int rc, tmp;
29960216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
29970216a895SLior David struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
29980216a895SLior David u16 sector_index, rf_module_index;
29990216a895SLior David u8 sector_type;
30000216a895SLior David u32 rf_modules_vec = 0;
30010216a895SLior David struct wmi_set_rf_sector_params_cmd cmd;
30020216a895SLior David struct {
30030216a895SLior David struct wmi_cmd_hdr wmi;
30040216a895SLior David struct wmi_set_rf_sector_params_done_event evt;
3005807b0860SAlexei Avshalom Lazar } __packed reply = {
3006807b0860SAlexei Avshalom Lazar .evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3007807b0860SAlexei Avshalom Lazar };
30080216a895SLior David struct nlattr *nl_cfg;
30090216a895SLior David struct wmi_rf_sector_info *si;
30100216a895SLior David
30110216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
30120216a895SLior David return -EOPNOTSUPP;
30130216a895SLior David
30148cb08174SJohannes Berg rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
30158cb08174SJohannes Berg data_len, wil_rf_sector_policy, NULL);
30160216a895SLior David if (rc) {
30170216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n");
30180216a895SLior David return rc;
30190216a895SLior David }
30200216a895SLior David
30210216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
30220216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] ||
30230216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) {
30240216a895SLior David wil_err(wil, "Invalid rf sector spec\n");
30250216a895SLior David return -EINVAL;
30260216a895SLior David }
30270216a895SLior David
30280216a895SLior David sector_index = nla_get_u16(
30290216a895SLior David tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
30300216a895SLior David if (sector_index >= WIL_MAX_RF_SECTORS) {
30310216a895SLior David wil_err(wil, "Invalid sector index %d\n", sector_index);
30320216a895SLior David return -EINVAL;
30330216a895SLior David }
30340216a895SLior David
30350216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
30360216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
30370216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type);
30380216a895SLior David return -EINVAL;
30390216a895SLior David }
30400216a895SLior David
30410216a895SLior David memset(&cmd, 0, sizeof(cmd));
30420216a895SLior David
30430216a895SLior David cmd.sector_idx = cpu_to_le16(sector_index);
30440216a895SLior David cmd.sector_type = sector_type;
30450216a895SLior David nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG],
30460216a895SLior David tmp) {
30478cb08174SJohannes Berg rc = nla_parse_nested_deprecated(tb2,
30488cb08174SJohannes Berg QCA_ATTR_DMG_RF_SECTOR_CFG_MAX,
30498cb08174SJohannes Berg nl_cfg,
30508cb08174SJohannes Berg wil_rf_sector_cfg_policy,
30510216a895SLior David NULL);
30520216a895SLior David if (rc) {
30530216a895SLior David wil_err(wil, "invalid sector cfg\n");
30540216a895SLior David return -EINVAL;
30550216a895SLior David }
30560216a895SLior David
30570216a895SLior David if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] ||
30580216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] ||
30590216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] ||
30600216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] ||
30610216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] ||
30620216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] ||
30630216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) {
30640216a895SLior David wil_err(wil, "missing cfg params\n");
30650216a895SLior David return -EINVAL;
30660216a895SLior David }
30670216a895SLior David
30680216a895SLior David rf_module_index = nla_get_u8(
30690216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]);
30700216a895SLior David if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) {
30710216a895SLior David wil_err(wil, "invalid RF module index %d\n",
30720216a895SLior David rf_module_index);
30730216a895SLior David return -EINVAL;
30740216a895SLior David }
30750216a895SLior David rf_modules_vec |= BIT(rf_module_index);
30760216a895SLior David si = &cmd.sectors_info[rf_module_index];
30770216a895SLior David si->etype0 = cpu_to_le32(nla_get_u32(
30780216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0]));
30790216a895SLior David si->etype1 = cpu_to_le32(nla_get_u32(
30800216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1]));
30810216a895SLior David si->etype2 = cpu_to_le32(nla_get_u32(
30820216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2]));
30830216a895SLior David si->psh_hi = cpu_to_le32(nla_get_u32(
30840216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI]));
30850216a895SLior David si->psh_lo = cpu_to_le32(nla_get_u32(
30860216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO]));
30870216a895SLior David si->dtype_swch_off = cpu_to_le32(nla_get_u32(
30880216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]));
30890216a895SLior David }
30900216a895SLior David
30910216a895SLior David cmd.rf_modules_vec = rf_modules_vec & 0xFF;
3092e00243faSLior David rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid,
3093e00243faSLior David &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
30940216a895SLior David &reply, sizeof(reply),
30950216a895SLior David 500);
30960216a895SLior David if (rc)
30970216a895SLior David return rc;
30980216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status);
30990216a895SLior David }
31000216a895SLior David
wil_rf_sector_get_selected(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)31010216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy,
31020216a895SLior David struct wireless_dev *wdev,
31030216a895SLior David const void *data, int data_len)
31040216a895SLior David {
31050216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev);
3106e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
31070216a895SLior David int rc;
31080216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
31090216a895SLior David u8 sector_type, mac_addr[ETH_ALEN];
31100216a895SLior David int cid = 0;
31110216a895SLior David struct wmi_get_selected_rf_sector_index_cmd cmd;
31120216a895SLior David struct {
31130216a895SLior David struct wmi_cmd_hdr wmi;
31140216a895SLior David struct wmi_get_selected_rf_sector_index_done_event evt;
3115807b0860SAlexei Avshalom Lazar } __packed reply = {
3116807b0860SAlexei Avshalom Lazar .evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3117807b0860SAlexei Avshalom Lazar };
31180216a895SLior David struct sk_buff *msg;
31190216a895SLior David
31200216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
31210216a895SLior David return -EOPNOTSUPP;
31220216a895SLior David
31238cb08174SJohannes Berg rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
31248cb08174SJohannes Berg data_len, wil_rf_sector_policy, NULL);
31250216a895SLior David if (rc) {
31260216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n");
31270216a895SLior David return rc;
31280216a895SLior David }
31290216a895SLior David
31300216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
31310216a895SLior David wil_err(wil, "Invalid rf sector spec\n");
31320216a895SLior David return -EINVAL;
31330216a895SLior David }
31340216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
31350216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
31360216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type);
31370216a895SLior David return -EINVAL;
31380216a895SLior David }
31390216a895SLior David
31400216a895SLior David if (tb[QCA_ATTR_MAC_ADDR]) {
31410216a895SLior David ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
3142e00243faSLior David cid = wil_find_cid(wil, vif->mid, mac_addr);
31430216a895SLior David if (cid < 0) {
31440216a895SLior David wil_err(wil, "invalid MAC address %pM\n", mac_addr);
31450216a895SLior David return -ENOENT;
31460216a895SLior David }
31470216a895SLior David } else {
31485bd60982SLior David if (test_bit(wil_vif_fwconnected, vif->status)) {
31490216a895SLior David wil_err(wil, "must specify MAC address when connected\n");
31500216a895SLior David return -EINVAL;
31510216a895SLior David }
31520216a895SLior David }
31530216a895SLior David
31540216a895SLior David memset(&cmd, 0, sizeof(cmd));
31550216a895SLior David cmd.cid = (u8)cid;
31560216a895SLior David cmd.sector_type = sector_type;
3157e00243faSLior David rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid,
31580216a895SLior David &cmd, sizeof(cmd),
31590216a895SLior David WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
31600216a895SLior David &reply, sizeof(reply),
31610216a895SLior David 500);
31620216a895SLior David if (rc)
31630216a895SLior David return rc;
31640216a895SLior David if (reply.evt.status) {
31650216a895SLior David wil_err(wil, "get rf selected sector cfg failed with status %d\n",
31660216a895SLior David reply.evt.status);
31670216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status);
31680216a895SLior David }
31690216a895SLior David
31700216a895SLior David msg = cfg80211_vendor_cmd_alloc_reply_skb(
31710216a895SLior David wiphy, 64 * WMI_MAX_RF_MODULES_NUM);
31720216a895SLior David if (!msg)
31730216a895SLior David return -ENOMEM;
31740216a895SLior David
31750216a895SLior David if (nla_put_u64_64bit(msg, QCA_ATTR_TSF,
31760216a895SLior David le64_to_cpu(reply.evt.tsf),
31770216a895SLior David QCA_ATTR_PAD) ||
31780216a895SLior David nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX,
31790216a895SLior David le16_to_cpu(reply.evt.sector_idx)))
31800216a895SLior David goto nla_put_failure;
31810216a895SLior David
31820216a895SLior David rc = cfg80211_vendor_cmd_reply(msg);
31830216a895SLior David return rc;
31840216a895SLior David nla_put_failure:
31850216a895SLior David kfree_skb(msg);
31860216a895SLior David return -ENOBUFS;
31870216a895SLior David }
31880216a895SLior David
wil_rf_sector_wmi_set_selected(struct wil6210_priv * wil,u8 mid,u16 sector_index,u8 sector_type,u8 cid)31890216a895SLior David static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
3190e00243faSLior David u8 mid, u16 sector_index,
31910216a895SLior David u8 sector_type, u8 cid)
31920216a895SLior David {
31930216a895SLior David struct wmi_set_selected_rf_sector_index_cmd cmd;
31940216a895SLior David struct {
31950216a895SLior David struct wmi_cmd_hdr wmi;
31960216a895SLior David struct wmi_set_selected_rf_sector_index_done_event evt;
3197807b0860SAlexei Avshalom Lazar } __packed reply = {
3198807b0860SAlexei Avshalom Lazar .evt = {.status = WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR},
3199807b0860SAlexei Avshalom Lazar };
32000216a895SLior David int rc;
32010216a895SLior David
32020216a895SLior David memset(&cmd, 0, sizeof(cmd));
32030216a895SLior David cmd.sector_idx = cpu_to_le16(sector_index);
32040216a895SLior David cmd.sector_type = sector_type;
32050216a895SLior David cmd.cid = (u8)cid;
3206e00243faSLior David rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid,
32070216a895SLior David &cmd, sizeof(cmd),
32080216a895SLior David WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
32090216a895SLior David &reply, sizeof(reply),
32100216a895SLior David 500);
32110216a895SLior David if (rc)
32120216a895SLior David return rc;
32130216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status);
32140216a895SLior David }
32150216a895SLior David
wil_rf_sector_set_selected(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)32160216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy,
32170216a895SLior David struct wireless_dev *wdev,
32180216a895SLior David const void *data, int data_len)
32190216a895SLior David {
32200216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev);
3221e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
32220216a895SLior David int rc;
32230216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
32240216a895SLior David u16 sector_index;
32250216a895SLior David u8 sector_type, mac_addr[ETH_ALEN], i;
32260216a895SLior David int cid = 0;
32270216a895SLior David
32280216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities))
32290216a895SLior David return -EOPNOTSUPP;
32300216a895SLior David
32318cb08174SJohannes Berg rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data,
32328cb08174SJohannes Berg data_len, wil_rf_sector_policy, NULL);
32330216a895SLior David if (rc) {
32340216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n");
32350216a895SLior David return rc;
32360216a895SLior David }
32370216a895SLior David
32380216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] ||
32390216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) {
32400216a895SLior David wil_err(wil, "Invalid rf sector spec\n");
32410216a895SLior David return -EINVAL;
32420216a895SLior David }
32430216a895SLior David
32440216a895SLior David sector_index = nla_get_u16(
32450216a895SLior David tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]);
32460216a895SLior David if (sector_index >= WIL_MAX_RF_SECTORS &&
32470216a895SLior David sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
32480216a895SLior David wil_err(wil, "Invalid sector index %d\n", sector_index);
32490216a895SLior David return -EINVAL;
32500216a895SLior David }
32510216a895SLior David
32520216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]);
32530216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) {
32540216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type);
32550216a895SLior David return -EINVAL;
32560216a895SLior David }
32570216a895SLior David
32580216a895SLior David if (tb[QCA_ATTR_MAC_ADDR]) {
32590216a895SLior David ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
32600216a895SLior David if (!is_broadcast_ether_addr(mac_addr)) {
3261e00243faSLior David cid = wil_find_cid(wil, vif->mid, mac_addr);
32620216a895SLior David if (cid < 0) {
32630216a895SLior David wil_err(wil, "invalid MAC address %pM\n",
32640216a895SLior David mac_addr);
32650216a895SLior David return -ENOENT;
32660216a895SLior David }
32670216a895SLior David } else {
32680216a895SLior David if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) {
32690216a895SLior David wil_err(wil, "broadcast MAC valid only with unlocking\n");
32700216a895SLior David return -EINVAL;
32710216a895SLior David }
32720216a895SLior David cid = -1;
32730216a895SLior David }
32740216a895SLior David } else {
32755bd60982SLior David if (test_bit(wil_vif_fwconnected, vif->status)) {
32760216a895SLior David wil_err(wil, "must specify MAC address when connected\n");
32770216a895SLior David return -EINVAL;
32780216a895SLior David }
32790216a895SLior David /* otherwise, using cid=0 for unassociated station */
32800216a895SLior David }
32810216a895SLior David
32820216a895SLior David if (cid >= 0) {
3283e00243faSLior David rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index,
32840216a895SLior David sector_type, cid);
32850216a895SLior David } else {
32860216a895SLior David /* unlock all cids */
32870216a895SLior David rc = wil_rf_sector_wmi_set_selected(
3288e00243faSLior David wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX,
3289e00243faSLior David sector_type, WIL_CID_ALL);
32900216a895SLior David if (rc == -EINVAL) {
3291ddf7afddSAhmad Masri for (i = 0; i < wil->max_assoc_sta; i++) {
3292e00243faSLior David if (wil->sta[i].mid != vif->mid)
3293e00243faSLior David continue;
32940216a895SLior David rc = wil_rf_sector_wmi_set_selected(
3295e00243faSLior David wil, vif->mid,
3296e00243faSLior David WMI_INVALID_RF_SECTOR_INDEX,
32970216a895SLior David sector_type, i);
32980216a895SLior David /* the FW will silently ignore and return
32990216a895SLior David * success for unused cid, so abort the loop
33000216a895SLior David * on any other error
33010216a895SLior David */
33020216a895SLior David if (rc) {
33030216a895SLior David wil_err(wil, "unlock cid %d failed with status %d\n",
33040216a895SLior David i, rc);
33050216a895SLior David break;
33060216a895SLior David }
33070216a895SLior David }
33080216a895SLior David }
33090216a895SLior David }
33100216a895SLior David
33110216a895SLior David return rc;
33120216a895SLior David }
3313