12be7d22fSVladimir Kondratiev /* 2849a564bSDedy Lansky * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. 39f38f286SLior David * Copyright (c) 2018, The Linux Foundation. All rights reserved. 42be7d22fSVladimir Kondratiev * 52be7d22fSVladimir Kondratiev * Permission to use, copy, modify, and/or distribute this software for any 62be7d22fSVladimir Kondratiev * purpose with or without fee is hereby granted, provided that the above 72be7d22fSVladimir Kondratiev * copyright notice and this permission notice appear in all copies. 82be7d22fSVladimir Kondratiev * 92be7d22fSVladimir Kondratiev * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 102be7d22fSVladimir Kondratiev * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 112be7d22fSVladimir Kondratiev * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 122be7d22fSVladimir Kondratiev * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 132be7d22fSVladimir Kondratiev * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 142be7d22fSVladimir Kondratiev * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 152be7d22fSVladimir Kondratiev * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 162be7d22fSVladimir Kondratiev */ 172be7d22fSVladimir Kondratiev 18a82553bbSVladimir Kondratiev #include <linux/etherdevice.h> 19949c2d00SJohannes Berg #include <linux/moduleparam.h> 200216a895SLior David #include <net/netlink.h> 214aebd3bdSLior David #include <net/cfg80211.h> 222be7d22fSVladimir Kondratiev #include "wil6210.h" 232be7d22fSVladimir Kondratiev #include "wmi.h" 247bfe9e22SLior David #include "fw.h" 252be7d22fSVladimir Kondratiev 26e6d68341SDedy Lansky #define WIL_MAX_ROC_DURATION_MS 5000 27e6d68341SDedy Lansky 28849a564bSDedy Lansky bool disable_ap_sme; 2978484c44SMaya Erez module_param(disable_ap_sme, bool, 0444); 30849a564bSDedy Lansky MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME"); 31849a564bSDedy Lansky 32d1fbf075SMaya Erez #ifdef CONFIG_PM 33d1fbf075SMaya Erez static struct wiphy_wowlan_support wil_wowlan_support = { 34d1fbf075SMaya Erez .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, 35d1fbf075SMaya Erez }; 36d1fbf075SMaya Erez #endif 37d1fbf075SMaya Erez 382be7d22fSVladimir Kondratiev #define CHAN60G(_channel, _flags) { \ 3957fbcce3SJohannes Berg .band = NL80211_BAND_60GHZ, \ 402be7d22fSVladimir Kondratiev .center_freq = 56160 + (2160 * (_channel)), \ 412be7d22fSVladimir Kondratiev .hw_value = (_channel), \ 422be7d22fSVladimir Kondratiev .flags = (_flags), \ 432be7d22fSVladimir Kondratiev .max_antenna_gain = 0, \ 442be7d22fSVladimir Kondratiev .max_power = 40, \ 452be7d22fSVladimir Kondratiev } 462be7d22fSVladimir Kondratiev 472be7d22fSVladimir Kondratiev static struct ieee80211_channel wil_60ghz_channels[] = { 482be7d22fSVladimir Kondratiev CHAN60G(1, 0), 492be7d22fSVladimir Kondratiev CHAN60G(2, 0), 502be7d22fSVladimir Kondratiev CHAN60G(3, 0), 512be7d22fSVladimir Kondratiev /* channel 4 not supported yet */ 522be7d22fSVladimir Kondratiev }; 532be7d22fSVladimir Kondratiev 540216a895SLior David /* Vendor id to be used in vendor specific command and events 550216a895SLior David * to user space. 560216a895SLior David * NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID, 570216a895SLior David * vendor subcmd definitions prefixed with QCA_NL80211_VENDOR_SUBCMD, and 580216a895SLior David * qca_wlan_vendor_attr is open source file src/common/qca-vendor.h in 590216a895SLior David * git://w1.fi/srv/git/hostap.git; the values here are just a copy of that 600216a895SLior David */ 610216a895SLior David 620216a895SLior David #define QCA_NL80211_VENDOR_ID 0x001374 630216a895SLior David 640216a895SLior David #define WIL_MAX_RF_SECTORS (128) 650216a895SLior David #define WIL_CID_ALL (0xff) 660216a895SLior David 670216a895SLior David enum qca_wlan_vendor_attr_rf_sector { 680216a895SLior David QCA_ATTR_MAC_ADDR = 6, 690216a895SLior David QCA_ATTR_PAD = 13, 700216a895SLior David QCA_ATTR_TSF = 29, 710216a895SLior David QCA_ATTR_DMG_RF_SECTOR_INDEX = 30, 720216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE = 31, 730216a895SLior David QCA_ATTR_DMG_RF_MODULE_MASK = 32, 740216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG = 33, 750216a895SLior David QCA_ATTR_DMG_RF_SECTOR_MAX, 760216a895SLior David }; 770216a895SLior David 780216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_type { 790216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE_RX, 800216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE_TX, 810216a895SLior David QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX 820216a895SLior David }; 830216a895SLior David 840216a895SLior David enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { 850216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, 860216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, 870216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, 880216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, 890216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, 900216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, 910216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, 920216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, 930216a895SLior David 940216a895SLior David /* keep last */ 950216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, 960216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_MAX = 970216a895SLior David QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 980216a895SLior David }; 990216a895SLior David 1000216a895SLior David static const struct 1010216a895SLior David nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = { 1020216a895SLior David [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN }, 1030216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 }, 1040216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 }, 1050216a895SLior David [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 }, 1060216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED }, 1070216a895SLior David }; 1080216a895SLior David 1090216a895SLior David static const struct 1100216a895SLior David nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = { 1110216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 }, 1120216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 }, 1130216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 }, 1140216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 }, 1150216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 }, 1160216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 }, 1170216a895SLior David [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 }, 1180216a895SLior David }; 1190216a895SLior David 1200216a895SLior David enum qca_nl80211_vendor_subcmds { 1210216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, 1220216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140, 1230216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141, 1240216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142, 1250216a895SLior David }; 1260216a895SLior David 1270216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy, 1280216a895SLior David struct wireless_dev *wdev, 1290216a895SLior David const void *data, int data_len); 1300216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy, 1310216a895SLior David struct wireless_dev *wdev, 1320216a895SLior David const void *data, int data_len); 1330216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy, 1340216a895SLior David struct wireless_dev *wdev, 1350216a895SLior David const void *data, int data_len); 1360216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy, 1370216a895SLior David struct wireless_dev *wdev, 1380216a895SLior David const void *data, int data_len); 1390216a895SLior David 1400216a895SLior David /* vendor specific commands */ 1410216a895SLior David static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { 1420216a895SLior David { 1430216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID, 1440216a895SLior David .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG, 1450216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV | 1460216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING, 1470216a895SLior David .doit = wil_rf_sector_get_cfg 1480216a895SLior David }, 1490216a895SLior David { 1500216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID, 1510216a895SLior David .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG, 1520216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV | 1530216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING, 1540216a895SLior David .doit = wil_rf_sector_set_cfg 1550216a895SLior David }, 1560216a895SLior David { 1570216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID, 1580216a895SLior David .info.subcmd = 1590216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR, 1600216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV | 1610216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING, 1620216a895SLior David .doit = wil_rf_sector_get_selected 1630216a895SLior David }, 1640216a895SLior David { 1650216a895SLior David .info.vendor_id = QCA_NL80211_VENDOR_ID, 1660216a895SLior David .info.subcmd = 1670216a895SLior David QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR, 1680216a895SLior David .flags = WIPHY_VENDOR_CMD_NEED_WDEV | 1690216a895SLior David WIPHY_VENDOR_CMD_NEED_RUNNING, 1700216a895SLior David .doit = wil_rf_sector_set_selected 1710216a895SLior David }, 1720216a895SLior David }; 1730216a895SLior David 1742be7d22fSVladimir Kondratiev static struct ieee80211_supported_band wil_band_60ghz = { 1752be7d22fSVladimir Kondratiev .channels = wil_60ghz_channels, 1762be7d22fSVladimir Kondratiev .n_channels = ARRAY_SIZE(wil_60ghz_channels), 1772be7d22fSVladimir Kondratiev .ht_cap = { 1782be7d22fSVladimir Kondratiev .ht_supported = true, 1792be7d22fSVladimir Kondratiev .cap = 0, /* TODO */ 1802be7d22fSVladimir Kondratiev .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ 1812be7d22fSVladimir Kondratiev .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ 1822be7d22fSVladimir Kondratiev .mcs = { 1832be7d22fSVladimir Kondratiev /* MCS 1..12 - SC PHY */ 1842be7d22fSVladimir Kondratiev .rx_mask = {0xfe, 0x1f}, /* 1..12 */ 1852be7d22fSVladimir Kondratiev .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ 1862be7d22fSVladimir Kondratiev }, 1872be7d22fSVladimir Kondratiev }, 1882be7d22fSVladimir Kondratiev }; 1892be7d22fSVladimir Kondratiev 1902be7d22fSVladimir Kondratiev static const struct ieee80211_txrx_stypes 1912be7d22fSVladimir Kondratiev wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { 1922be7d22fSVladimir Kondratiev [NL80211_IFTYPE_STATION] = { 1932be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1942be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 1952be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 1962be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 1972be7d22fSVladimir Kondratiev }, 1982be7d22fSVladimir Kondratiev [NL80211_IFTYPE_AP] = { 1992be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 200849a564bSDedy Lansky BIT(IEEE80211_STYPE_PROBE_RESP >> 4) | 201849a564bSDedy Lansky BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) | 202849a564bSDedy Lansky BIT(IEEE80211_STYPE_DISASSOC >> 4), 2032be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 204849a564bSDedy Lansky BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | 205849a564bSDedy Lansky BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | 206849a564bSDedy Lansky BIT(IEEE80211_STYPE_DISASSOC >> 4) | 207849a564bSDedy Lansky BIT(IEEE80211_STYPE_AUTH >> 4) | 208849a564bSDedy Lansky BIT(IEEE80211_STYPE_DEAUTH >> 4) | 209849a564bSDedy Lansky BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) 2102be7d22fSVladimir Kondratiev }, 2112be7d22fSVladimir Kondratiev [NL80211_IFTYPE_P2P_CLIENT] = { 2122be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 2132be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 2142be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 2152be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 2162be7d22fSVladimir Kondratiev }, 2172be7d22fSVladimir Kondratiev [NL80211_IFTYPE_P2P_GO] = { 2182be7d22fSVladimir Kondratiev .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 2192be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 2202be7d22fSVladimir Kondratiev .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 2212be7d22fSVladimir Kondratiev BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 2222be7d22fSVladimir Kondratiev }, 2234332cac1SLior David [NL80211_IFTYPE_P2P_DEVICE] = { 2244332cac1SLior David .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 2254332cac1SLior David BIT(IEEE80211_STYPE_PROBE_RESP >> 4), 2264332cac1SLior David .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 2274332cac1SLior David BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 2284332cac1SLior David }, 2292be7d22fSVladimir Kondratiev }; 2302be7d22fSVladimir Kondratiev 2312be7d22fSVladimir Kondratiev static const u32 wil_cipher_suites[] = { 2322be7d22fSVladimir Kondratiev WLAN_CIPHER_SUITE_GCMP, 2332be7d22fSVladimir Kondratiev }; 2342be7d22fSVladimir Kondratiev 23558527421SVladimir Kondratiev static const char * const key_usage_str[] = { 23658527421SVladimir Kondratiev [WMI_KEY_USE_PAIRWISE] = "PTK", 23758527421SVladimir Kondratiev [WMI_KEY_USE_RX_GROUP] = "RX_GTK", 23858527421SVladimir Kondratiev [WMI_KEY_USE_TX_GROUP] = "TX_GTK", 23958527421SVladimir Kondratiev }; 24058527421SVladimir Kondratiev 2412be7d22fSVladimir Kondratiev int wil_iftype_nl2wmi(enum nl80211_iftype type) 2422be7d22fSVladimir Kondratiev { 2432be7d22fSVladimir Kondratiev static const struct { 2442be7d22fSVladimir Kondratiev enum nl80211_iftype nl; 2452be7d22fSVladimir Kondratiev enum wmi_network_type wmi; 2462be7d22fSVladimir Kondratiev } __nl2wmi[] = { 2472be7d22fSVladimir Kondratiev {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, 2482be7d22fSVladimir Kondratiev {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, 2492be7d22fSVladimir Kondratiev {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, 2502be7d22fSVladimir Kondratiev {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, 2512be7d22fSVladimir Kondratiev {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, 2522be7d22fSVladimir Kondratiev {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ 2532be7d22fSVladimir Kondratiev }; 2542be7d22fSVladimir Kondratiev uint i; 2552be7d22fSVladimir Kondratiev 2562be7d22fSVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { 2572be7d22fSVladimir Kondratiev if (__nl2wmi[i].nl == type) 2582be7d22fSVladimir Kondratiev return __nl2wmi[i].wmi; 2592be7d22fSVladimir Kondratiev } 2602be7d22fSVladimir Kondratiev 2612be7d22fSVladimir Kondratiev return -EOPNOTSUPP; 2622be7d22fSVladimir Kondratiev } 2632be7d22fSVladimir Kondratiev 264e00243faSLior David int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid, 265ef28afdbSVladimir Kondratiev struct station_info *sinfo) 2662be7d22fSVladimir Kondratiev { 267e00243faSLior David struct wil6210_priv *wil = vif_to_wil(vif); 2682be7d22fSVladimir Kondratiev struct wmi_notify_req_cmd cmd = { 2693df2cd36SVladimir Kondratiev .cid = cid, 2702be7d22fSVladimir Kondratiev .interval_usec = 0, 2712be7d22fSVladimir Kondratiev }; 272ef28afdbSVladimir Kondratiev struct { 273b874ddecSLior David struct wmi_cmd_hdr wmi; 274ef28afdbSVladimir Kondratiev struct wmi_notify_req_done_event evt; 275ef28afdbSVladimir Kondratiev } __packed reply; 276c8b78b5fSVladimir Kondratiev struct wil_net_stats *stats = &wil->sta[cid].stats; 277ef28afdbSVladimir Kondratiev int rc; 2782be7d22fSVladimir Kondratiev 279e00243faSLior David rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd), 280ef28afdbSVladimir Kondratiev WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); 2812be7d22fSVladimir Kondratiev if (rc) 2822be7d22fSVladimir Kondratiev return rc; 2832be7d22fSVladimir Kondratiev 284e00243faSLior David wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n" 285c8b78b5fSVladimir Kondratiev " MCS %d TSF 0x%016llx\n" 28630868f5dSDedy Lansky " BF status 0x%08x RSSI %d SQI %d%%\n" 287c8b78b5fSVladimir Kondratiev " Tx Tpt %d goodput %d Rx goodput %d\n" 288c8b78b5fSVladimir Kondratiev " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", 289e00243faSLior David cid, vif->mid, le16_to_cpu(reply.evt.bf_mcs), 290c8b78b5fSVladimir Kondratiev le64_to_cpu(reply.evt.tsf), reply.evt.status, 29130868f5dSDedy Lansky reply.evt.rssi, 292b8b33a3aSVladimir Kondratiev reply.evt.sqi, 293c8b78b5fSVladimir Kondratiev le32_to_cpu(reply.evt.tx_tpt), 294c8b78b5fSVladimir Kondratiev le32_to_cpu(reply.evt.tx_goodput), 295c8b78b5fSVladimir Kondratiev le32_to_cpu(reply.evt.rx_goodput), 296c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.my_rx_sector), 297c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.my_tx_sector), 298c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.other_rx_sector), 299c8b78b5fSVladimir Kondratiev le16_to_cpu(reply.evt.other_tx_sector)); 300c8b78b5fSVladimir Kondratiev 3012be7d22fSVladimir Kondratiev sinfo->generation = wil->sinfo_gen; 3022be7d22fSVladimir Kondratiev 303319090bfSJohannes Berg sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | 304319090bfSJohannes Berg BIT(NL80211_STA_INFO_TX_BYTES) | 305319090bfSJohannes Berg BIT(NL80211_STA_INFO_RX_PACKETS) | 306319090bfSJohannes Berg BIT(NL80211_STA_INFO_TX_PACKETS) | 307319090bfSJohannes Berg BIT(NL80211_STA_INFO_RX_BITRATE) | 308319090bfSJohannes Berg BIT(NL80211_STA_INFO_TX_BITRATE) | 309319090bfSJohannes Berg BIT(NL80211_STA_INFO_RX_DROP_MISC) | 310319090bfSJohannes Berg BIT(NL80211_STA_INFO_TX_FAILED); 311c8b78b5fSVladimir Kondratiev 3120c1eca4eSJohannes Berg sinfo->txrate.flags = RATE_INFO_FLAGS_60G; 313ef28afdbSVladimir Kondratiev sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); 314c8b78b5fSVladimir Kondratiev sinfo->rxrate.mcs = stats->last_mcs_rx; 315c8b78b5fSVladimir Kondratiev sinfo->rx_bytes = stats->rx_bytes; 316c8b78b5fSVladimir Kondratiev sinfo->rx_packets = stats->rx_packets; 317c8b78b5fSVladimir Kondratiev sinfo->rx_dropped_misc = stats->rx_dropped; 318c8b78b5fSVladimir Kondratiev sinfo->tx_bytes = stats->tx_bytes; 319c8b78b5fSVladimir Kondratiev sinfo->tx_packets = stats->tx_packets; 320c8b78b5fSVladimir Kondratiev sinfo->tx_failed = stats->tx_errors; 3212be7d22fSVladimir Kondratiev 3225bd60982SLior David if (test_bit(wil_vif_fwconnected, vif->status)) { 323319090bfSJohannes Berg sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); 32430868f5dSDedy Lansky if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, 32530868f5dSDedy Lansky wil->fw_capabilities)) 32630868f5dSDedy Lansky sinfo->signal = reply.evt.rssi; 32730868f5dSDedy Lansky else 328b8b33a3aSVladimir Kondratiev sinfo->signal = reply.evt.sqi; 3292be7d22fSVladimir Kondratiev } 3302be7d22fSVladimir Kondratiev 331ef28afdbSVladimir Kondratiev return rc; 332ef28afdbSVladimir Kondratiev } 333ef28afdbSVladimir Kondratiev 334ef28afdbSVladimir Kondratiev static int wil_cfg80211_get_station(struct wiphy *wiphy, 335ef28afdbSVladimir Kondratiev struct net_device *ndev, 3363b3a0162SJohannes Berg const u8 *mac, struct station_info *sinfo) 337ef28afdbSVladimir Kondratiev { 338e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 339ef28afdbSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 340ef28afdbSVladimir Kondratiev int rc; 341ef28afdbSVladimir Kondratiev 342e00243faSLior David int cid = wil_find_cid(wil, vif->mid, mac); 343ef28afdbSVladimir Kondratiev 344e00243faSLior David wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid, 345e00243faSLior David vif->mid); 346ef28afdbSVladimir Kondratiev if (cid < 0) 347c14c5d99SVladimir Kondratiev return cid; 348ef28afdbSVladimir Kondratiev 349e00243faSLior David rc = wil_cid_fill_sinfo(vif, cid, sinfo); 350ef28afdbSVladimir Kondratiev 351ef28afdbSVladimir Kondratiev return rc; 352ef28afdbSVladimir Kondratiev } 353ef28afdbSVladimir Kondratiev 354ef28afdbSVladimir Kondratiev /* 355e00243faSLior David * Find @idx-th active STA for specific MID for station dump. 356ef28afdbSVladimir Kondratiev */ 357e00243faSLior David static int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx) 358ef28afdbSVladimir Kondratiev { 359ef28afdbSVladimir Kondratiev int i; 360ef28afdbSVladimir Kondratiev 361ef28afdbSVladimir Kondratiev for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { 362ef28afdbSVladimir Kondratiev if (wil->sta[i].status == wil_sta_unused) 363ef28afdbSVladimir Kondratiev continue; 364e00243faSLior David if (wil->sta[i].mid != mid) 365e00243faSLior David continue; 366ef28afdbSVladimir Kondratiev if (idx == 0) 367ef28afdbSVladimir Kondratiev return i; 368ef28afdbSVladimir Kondratiev idx--; 369ef28afdbSVladimir Kondratiev } 370ef28afdbSVladimir Kondratiev 371ef28afdbSVladimir Kondratiev return -ENOENT; 372ef28afdbSVladimir Kondratiev } 373ef28afdbSVladimir Kondratiev 374ef28afdbSVladimir Kondratiev static int wil_cfg80211_dump_station(struct wiphy *wiphy, 375ef28afdbSVladimir Kondratiev struct net_device *dev, int idx, 376ef28afdbSVladimir Kondratiev u8 *mac, struct station_info *sinfo) 377ef28afdbSVladimir Kondratiev { 378e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 379ef28afdbSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 380ef28afdbSVladimir Kondratiev int rc; 381e00243faSLior David int cid = wil_find_cid_by_idx(wil, vif->mid, idx); 382ef28afdbSVladimir Kondratiev 383ef28afdbSVladimir Kondratiev if (cid < 0) 384ef28afdbSVladimir Kondratiev return -ENOENT; 385ef28afdbSVladimir Kondratiev 386a82553bbSVladimir Kondratiev ether_addr_copy(mac, wil->sta[cid].addr); 387e00243faSLior David wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid, 388e00243faSLior David vif->mid); 389ef28afdbSVladimir Kondratiev 390e00243faSLior David rc = wil_cid_fill_sinfo(vif, cid, sinfo); 391ef28afdbSVladimir Kondratiev 392ef28afdbSVladimir Kondratiev return rc; 3932be7d22fSVladimir Kondratiev } 3942be7d22fSVladimir Kondratiev 39569fecf59SLior David static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy, 39669fecf59SLior David struct wireless_dev *wdev) 39769fecf59SLior David { 39869fecf59SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy); 39969fecf59SLior David 40069fecf59SLior David wil_dbg_misc(wil, "start_p2p_device: entered\n"); 401e00243faSLior David wil->p2p_dev_started = 1; 40269fecf59SLior David return 0; 40369fecf59SLior David } 40469fecf59SLior David 40569fecf59SLior David static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, 40669fecf59SLior David struct wireless_dev *wdev) 40769fecf59SLior David { 40869fecf59SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy); 40969fecf59SLior David 410e00243faSLior David if (!wil->p2p_dev_started) 41169fecf59SLior David return; 41269fecf59SLior David 41369fecf59SLior David wil_dbg_misc(wil, "stop_p2p_device: entered\n"); 41469fecf59SLior David mutex_lock(&wil->mutex); 415404bbb3cSLior David mutex_lock(&wil->vif_mutex); 41669fecf59SLior David wil_p2p_stop_radio_operations(wil); 417e00243faSLior David wil->p2p_dev_started = 0; 418404bbb3cSLior David mutex_unlock(&wil->vif_mutex); 41969fecf59SLior David mutex_unlock(&wil->mutex); 42069fecf59SLior David } 42169fecf59SLior David 4224aebd3bdSLior David static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil, 4234aebd3bdSLior David enum nl80211_iftype new_type) 4244aebd3bdSLior David { 4254aebd3bdSLior David int i; 4264aebd3bdSLior David struct wireless_dev *wdev; 4274aebd3bdSLior David struct iface_combination_params params = { 4284aebd3bdSLior David .num_different_channels = 1, 4294aebd3bdSLior David }; 4304aebd3bdSLior David 4314aebd3bdSLior David for (i = 0; i < wil->max_vifs; i++) { 4324aebd3bdSLior David if (wil->vifs[i]) { 4334aebd3bdSLior David wdev = vif_to_wdev(wil->vifs[i]); 4344aebd3bdSLior David params.iftype_num[wdev->iftype]++; 4354aebd3bdSLior David } 4364aebd3bdSLior David } 4374aebd3bdSLior David params.iftype_num[new_type]++; 4384aebd3bdSLior David return cfg80211_check_combinations(wil->wiphy, ¶ms); 4394aebd3bdSLior David } 4404aebd3bdSLior David 4414aebd3bdSLior David static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil, 4424aebd3bdSLior David struct wil6210_vif *vif, 4434aebd3bdSLior David enum nl80211_iftype new_type) 4444aebd3bdSLior David { 4454aebd3bdSLior David int i, ret = 0; 4464aebd3bdSLior David struct wireless_dev *wdev; 4474aebd3bdSLior David struct iface_combination_params params = { 4484aebd3bdSLior David .num_different_channels = 1, 4494aebd3bdSLior David }; 4504aebd3bdSLior David bool check_combos = false; 4514aebd3bdSLior David 4524aebd3bdSLior David for (i = 0; i < wil->max_vifs; i++) { 4534aebd3bdSLior David struct wil6210_vif *vif_pos = wil->vifs[i]; 4544aebd3bdSLior David 4554aebd3bdSLior David if (vif_pos && vif != vif_pos) { 4564aebd3bdSLior David wdev = vif_to_wdev(vif_pos); 4574aebd3bdSLior David params.iftype_num[wdev->iftype]++; 4584aebd3bdSLior David check_combos = true; 4594aebd3bdSLior David } 4604aebd3bdSLior David } 4614aebd3bdSLior David 4624aebd3bdSLior David if (check_combos) { 4634aebd3bdSLior David params.iftype_num[new_type]++; 4644aebd3bdSLior David ret = cfg80211_check_combinations(wil->wiphy, ¶ms); 4654aebd3bdSLior David } 4664aebd3bdSLior David return ret; 4674aebd3bdSLior David } 4684aebd3bdSLior David 4694332cac1SLior David static struct wireless_dev * 4704332cac1SLior David wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, 4714332cac1SLior David unsigned char name_assign_type, 4724332cac1SLior David enum nl80211_iftype type, 473818a986eSJohannes Berg struct vif_params *params) 4744332cac1SLior David { 4754332cac1SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy); 4764aebd3bdSLior David struct net_device *ndev_main = wil->main_ndev, *ndev; 4779f38f286SLior David struct wil6210_vif *vif; 4784aebd3bdSLior David struct wireless_dev *p2p_wdev, *wdev; 4794aebd3bdSLior David int rc; 4804332cac1SLior David 4814aebd3bdSLior David wil_dbg_misc(wil, "add_iface, type %d\n", type); 4824332cac1SLior David 4834aebd3bdSLior David /* P2P device is not a real virtual interface, it is a management-only 4844aebd3bdSLior David * interface that shares the main interface. 4854aebd3bdSLior David * Skip concurrency checks here. 4864aebd3bdSLior David */ 4874aebd3bdSLior David if (type == NL80211_IFTYPE_P2P_DEVICE) { 4884332cac1SLior David if (wil->p2p_wdev) { 489af3db60aSLazar Alexei wil_err(wil, "P2P_DEVICE interface already created\n"); 4904332cac1SLior David return ERR_PTR(-EINVAL); 4914332cac1SLior David } 4924332cac1SLior David 4935bd60982SLior David p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL); 4945bd60982SLior David if (!p2p_wdev) 4954332cac1SLior David return ERR_PTR(-ENOMEM); 4964332cac1SLior David 4974332cac1SLior David p2p_wdev->iftype = type; 4984332cac1SLior David p2p_wdev->wiphy = wiphy; 4994332cac1SLior David /* use our primary ethernet address */ 5004aebd3bdSLior David ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr); 5014332cac1SLior David 5024332cac1SLior David wil->p2p_wdev = p2p_wdev; 5034332cac1SLior David 5044332cac1SLior David return p2p_wdev; 5054332cac1SLior David } 5064332cac1SLior David 5074aebd3bdSLior David if (!wil->wiphy->n_iface_combinations) { 5084aebd3bdSLior David wil_err(wil, "virtual interfaces not supported\n"); 5094aebd3bdSLior David return ERR_PTR(-EINVAL); 5104aebd3bdSLior David } 5114aebd3bdSLior David 5124aebd3bdSLior David rc = wil_cfg80211_validate_add_iface(wil, type); 5134aebd3bdSLior David if (rc) { 5144aebd3bdSLior David wil_err(wil, "iface validation failed, err=%d\n", rc); 5154aebd3bdSLior David return ERR_PTR(rc); 5164aebd3bdSLior David } 5174aebd3bdSLior David 5184aebd3bdSLior David vif = wil_vif_alloc(wil, name, name_assign_type, type); 5194aebd3bdSLior David if (IS_ERR(vif)) 5204aebd3bdSLior David return ERR_CAST(vif); 5214aebd3bdSLior David 5224aebd3bdSLior David ndev = vif_to_ndev(vif); 5234aebd3bdSLior David ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr); 5244aebd3bdSLior David if (is_valid_ether_addr(params->macaddr)) { 5254aebd3bdSLior David ether_addr_copy(ndev->dev_addr, params->macaddr); 5264aebd3bdSLior David } else { 5274aebd3bdSLior David ether_addr_copy(ndev->dev_addr, ndev_main->perm_addr); 5284aebd3bdSLior David ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << vif->mid)) | 5294aebd3bdSLior David 0x2; /* locally administered */ 5304aebd3bdSLior David } 5314aebd3bdSLior David wdev = vif_to_wdev(vif); 5324aebd3bdSLior David ether_addr_copy(wdev->address, ndev->dev_addr); 5334aebd3bdSLior David 5344aebd3bdSLior David rc = wil_vif_add(wil, vif); 5354aebd3bdSLior David if (rc) 5364aebd3bdSLior David goto out; 5374aebd3bdSLior David 5384aebd3bdSLior David wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n", 5394aebd3bdSLior David vif->mid, type, wdev->address); 5404aebd3bdSLior David return wdev; 5414aebd3bdSLior David out: 5424aebd3bdSLior David wil_vif_free(vif); 5434aebd3bdSLior David return ERR_PTR(rc); 5444aebd3bdSLior David } 5454aebd3bdSLior David 5463ada9314SLior David int wil_vif_prepare_stop(struct wil6210_vif *vif) 5474aebd3bdSLior David { 5483ada9314SLior David struct wil6210_priv *wil = vif_to_wil(vif); 5494aebd3bdSLior David struct wireless_dev *wdev = vif_to_wdev(vif); 5504aebd3bdSLior David struct net_device *ndev; 5514aebd3bdSLior David int rc; 5524aebd3bdSLior David 5534aebd3bdSLior David if (wdev->iftype != NL80211_IFTYPE_AP) 5544aebd3bdSLior David return 0; 5554aebd3bdSLior David 5564aebd3bdSLior David ndev = vif_to_ndev(vif); 5574aebd3bdSLior David if (netif_carrier_ok(ndev)) { 5584aebd3bdSLior David rc = wmi_pcp_stop(vif); 5594aebd3bdSLior David if (rc) { 5604aebd3bdSLior David wil_info(wil, "failed to stop AP, status %d\n", 5614aebd3bdSLior David rc); 5624aebd3bdSLior David /* continue */ 5634aebd3bdSLior David } 5643ada9314SLior David wil_bcast_fini(vif); 5654aebd3bdSLior David netif_carrier_off(ndev); 5664aebd3bdSLior David } 5674aebd3bdSLior David 5684aebd3bdSLior David return 0; 5694aebd3bdSLior David } 5704aebd3bdSLior David 5714332cac1SLior David static int wil_cfg80211_del_iface(struct wiphy *wiphy, 5724332cac1SLior David struct wireless_dev *wdev) 5734332cac1SLior David { 5744332cac1SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy); 5754aebd3bdSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 5764aebd3bdSLior David int rc; 5774332cac1SLior David 578af3db60aSLazar Alexei wil_dbg_misc(wil, "del_iface\n"); 5794332cac1SLior David 5804aebd3bdSLior David if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { 5814332cac1SLior David if (wdev != wil->p2p_wdev) { 5824aebd3bdSLior David wil_err(wil, "delete of incorrect interface 0x%p\n", 5834aebd3bdSLior David wdev); 5844332cac1SLior David return -EINVAL; 5854332cac1SLior David } 5864332cac1SLior David 58769fecf59SLior David wil_cfg80211_stop_p2p_device(wiphy, wdev); 5884332cac1SLior David wil_p2p_wdev_free(wil); 5894332cac1SLior David return 0; 5904332cac1SLior David } 5914332cac1SLior David 5924aebd3bdSLior David if (vif->mid == 0) { 5934aebd3bdSLior David wil_err(wil, "cannot remove the main interface\n"); 5944aebd3bdSLior David return -EINVAL; 5954aebd3bdSLior David } 5964aebd3bdSLior David 5973ada9314SLior David rc = wil_vif_prepare_stop(vif); 5984aebd3bdSLior David if (rc) 5994aebd3bdSLior David goto out; 6004aebd3bdSLior David 6014aebd3bdSLior David wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n", 6024aebd3bdSLior David vif->mid, wdev->iftype, wdev->address); 6034aebd3bdSLior David 6044aebd3bdSLior David wil_vif_remove(wil, vif->mid); 6054aebd3bdSLior David out: 6064aebd3bdSLior David return rc; 6074aebd3bdSLior David } 6084aebd3bdSLior David 6092be7d22fSVladimir Kondratiev static int wil_cfg80211_change_iface(struct wiphy *wiphy, 6102be7d22fSVladimir Kondratiev struct net_device *ndev, 611818a986eSJohannes Berg enum nl80211_iftype type, 6122be7d22fSVladimir Kondratiev struct vif_params *params) 6132be7d22fSVladimir Kondratiev { 6142be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 615e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 616e00243faSLior David struct wireless_dev *wdev = vif_to_wdev(vif); 617e6d68341SDedy Lansky int rc; 6183ada9314SLior David bool fw_reset = false; 619e6d68341SDedy Lansky 620af3db60aSLazar Alexei wil_dbg_misc(wil, "change_iface: type=%d\n", type); 621e6d68341SDedy Lansky 6224aebd3bdSLior David if (wiphy->n_iface_combinations) { 6234aebd3bdSLior David rc = wil_cfg80211_validate_change_iface(wil, vif, type); 6244aebd3bdSLior David if (rc) { 6254aebd3bdSLior David wil_err(wil, "iface validation failed, err=%d\n", rc); 6264aebd3bdSLior David return rc; 6274aebd3bdSLior David } 6284aebd3bdSLior David } 6294aebd3bdSLior David 6304aebd3bdSLior David /* do not reset FW when there are active VIFs, 6314aebd3bdSLior David * because it can cause significant disruption 6324aebd3bdSLior David */ 6333ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, true, false) && 6344aebd3bdSLior David netif_running(ndev) && !wil_is_recovery_blocked(wil)) { 635e6d68341SDedy Lansky wil_dbg_misc(wil, "interface is up. resetting...\n"); 636e6d68341SDedy Lansky mutex_lock(&wil->mutex); 637e6d68341SDedy Lansky __wil_down(wil); 638e6d68341SDedy Lansky rc = __wil_up(wil); 639e6d68341SDedy Lansky mutex_unlock(&wil->mutex); 640e6d68341SDedy Lansky 641e6d68341SDedy Lansky if (rc) 642e6d68341SDedy Lansky return rc; 6433ada9314SLior David fw_reset = true; 644e6d68341SDedy Lansky } 6452be7d22fSVladimir Kondratiev 6462be7d22fSVladimir Kondratiev switch (type) { 6472be7d22fSVladimir Kondratiev case NL80211_IFTYPE_STATION: 6482be7d22fSVladimir Kondratiev case NL80211_IFTYPE_AP: 6492be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 6502be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_GO: 6512be7d22fSVladimir Kondratiev break; 6522be7d22fSVladimir Kondratiev case NL80211_IFTYPE_MONITOR: 653818a986eSJohannes Berg if (params->flags) 654818a986eSJohannes Berg wil->monitor_flags = params->flags; 6552be7d22fSVladimir Kondratiev break; 6562be7d22fSVladimir Kondratiev default: 6572be7d22fSVladimir Kondratiev return -EOPNOTSUPP; 6582be7d22fSVladimir Kondratiev } 6592be7d22fSVladimir Kondratiev 6603ada9314SLior David if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) { 6613ada9314SLior David if (!fw_reset) 6623ada9314SLior David wil_vif_prepare_stop(vif); 6634aebd3bdSLior David rc = wmi_port_delete(wil, vif->mid); 6644aebd3bdSLior David if (rc) 6654aebd3bdSLior David return rc; 6664aebd3bdSLior David rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type); 6674aebd3bdSLior David if (rc) 6684aebd3bdSLior David return rc; 6694aebd3bdSLior David } 6702be7d22fSVladimir Kondratiev 6714aebd3bdSLior David wdev->iftype = type; 6722be7d22fSVladimir Kondratiev return 0; 6732be7d22fSVladimir Kondratiev } 6742be7d22fSVladimir Kondratiev 6752be7d22fSVladimir Kondratiev static int wil_cfg80211_scan(struct wiphy *wiphy, 6762be7d22fSVladimir Kondratiev struct cfg80211_scan_request *request) 6772be7d22fSVladimir Kondratiev { 6782be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 6794332cac1SLior David struct wireless_dev *wdev = request->wdev; 680e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 6812be7d22fSVladimir Kondratiev struct { 6822be7d22fSVladimir Kondratiev struct wmi_start_scan_cmd cmd; 6832be7d22fSVladimir Kondratiev u16 chnl[4]; 6842be7d22fSVladimir Kondratiev } __packed cmd; 6852be7d22fSVladimir Kondratiev uint i, n; 686ed6f9dc6SVladimir Kondratiev int rc; 6872be7d22fSVladimir Kondratiev 688af3db60aSLazar Alexei wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype); 689e6d68341SDedy Lansky 6902be7d22fSVladimir Kondratiev /* check we are client side */ 6912be7d22fSVladimir Kondratiev switch (wdev->iftype) { 6922be7d22fSVladimir Kondratiev case NL80211_IFTYPE_STATION: 6932be7d22fSVladimir Kondratiev case NL80211_IFTYPE_P2P_CLIENT: 6944332cac1SLior David case NL80211_IFTYPE_P2P_DEVICE: 6952be7d22fSVladimir Kondratiev break; 6962be7d22fSVladimir Kondratiev default: 6972be7d22fSVladimir Kondratiev return -EOPNOTSUPP; 6982be7d22fSVladimir Kondratiev } 6992be7d22fSVladimir Kondratiev 7002be7d22fSVladimir Kondratiev /* FW don't support scan after connection attempt */ 7019419b6a2SVladimir Kondratiev if (test_bit(wil_status_dontscan, wil->status)) { 702e83eb2fcSVladimir Kondratiev wil_err(wil, "Can't scan now\n"); 7032be7d22fSVladimir Kondratiev return -EBUSY; 7042be7d22fSVladimir Kondratiev } 7052be7d22fSVladimir Kondratiev 706bb6743f7SLior David mutex_lock(&wil->mutex); 707bb6743f7SLior David 708404bbb3cSLior David mutex_lock(&wil->vif_mutex); 709e00243faSLior David if (vif->scan_request || vif->p2p.discovery_started) { 710bb6743f7SLior David wil_err(wil, "Already scanning\n"); 711404bbb3cSLior David mutex_unlock(&wil->vif_mutex); 712bb6743f7SLior David rc = -EAGAIN; 713bb6743f7SLior David goto out; 714bb6743f7SLior David } 715404bbb3cSLior David mutex_unlock(&wil->vif_mutex); 716bb6743f7SLior David 7174a0e45a7SLior David if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { 718e00243faSLior David if (!wil->p2p_dev_started) { 719eb57a5b3SLior David wil_err(wil, "P2P search requested on stopped P2P device\n"); 720bb6743f7SLior David rc = -EIO; 721bb6743f7SLior David goto out; 722eb57a5b3SLior David } 7234a0e45a7SLior David /* social scan on P2P_DEVICE is handled as p2p search */ 7244a0e45a7SLior David if (wil_p2p_is_social_scan(request)) { 725e00243faSLior David vif->scan_request = request; 726e00243faSLior David if (vif->mid == 0) 7274332cac1SLior David wil->radio_wdev = wdev; 728e00243faSLior David rc = wil_p2p_search(vif, request); 7294332cac1SLior David if (rc) { 730e00243faSLior David if (vif->mid == 0) 731e00243faSLior David wil->radio_wdev = 732e00243faSLior David wil->main_ndev->ieee80211_ptr; 733e00243faSLior David vif->scan_request = NULL; 7344332cac1SLior David } 735bb6743f7SLior David goto out; 736e6d68341SDedy Lansky } 7374a0e45a7SLior David } 738e6d68341SDedy Lansky 739e00243faSLior David (void)wil_p2p_stop_discovery(vif); 740e6d68341SDedy Lansky 7412a91d7d0SVladimir Kondratiev wil_dbg_misc(wil, "Start scan_request 0x%p\n", request); 7428e52fe30SHamad Kadmany wil_dbg_misc(wil, "SSID count: %d", request->n_ssids); 7438e52fe30SHamad Kadmany 7448e52fe30SHamad Kadmany for (i = 0; i < request->n_ssids; i++) { 7458e52fe30SHamad Kadmany wil_dbg_misc(wil, "SSID[%d]", i); 7465eb443e9SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, 7478e52fe30SHamad Kadmany request->ssids[i].ssid, 7485eb443e9SDedy Lansky request->ssids[i].ssid_len, true); 7498e52fe30SHamad Kadmany } 7508e52fe30SHamad Kadmany 7518e52fe30SHamad Kadmany if (request->n_ssids) 752e00243faSLior David rc = wmi_set_ssid(vif, request->ssids[0].ssid_len, 7538e52fe30SHamad Kadmany request->ssids[0].ssid); 7548e52fe30SHamad Kadmany else 755e00243faSLior David rc = wmi_set_ssid(vif, 0, NULL); 7568e52fe30SHamad Kadmany 7578e52fe30SHamad Kadmany if (rc) { 7588e52fe30SHamad Kadmany wil_err(wil, "set SSID for scan request failed: %d\n", rc); 759bb6743f7SLior David goto out; 7608e52fe30SHamad Kadmany } 7618e52fe30SHamad Kadmany 762e00243faSLior David vif->scan_request = request; 763e00243faSLior David mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO); 7642be7d22fSVladimir Kondratiev 7652be7d22fSVladimir Kondratiev memset(&cmd, 0, sizeof(cmd)); 76674997a53SLior David cmd.cmd.scan_type = WMI_ACTIVE_SCAN; 7672be7d22fSVladimir Kondratiev cmd.cmd.num_channels = 0; 7682be7d22fSVladimir Kondratiev n = min(request->n_channels, 4U); 7692be7d22fSVladimir Kondratiev for (i = 0; i < n; i++) { 7702be7d22fSVladimir Kondratiev int ch = request->channels[i]->hw_value; 7718fe59627SVladimir Kondratiev 7722be7d22fSVladimir Kondratiev if (ch == 0) { 7732be7d22fSVladimir Kondratiev wil_err(wil, 7742be7d22fSVladimir Kondratiev "Scan requested for unknown frequency %dMhz\n", 7752be7d22fSVladimir Kondratiev request->channels[i]->center_freq); 7762be7d22fSVladimir Kondratiev continue; 7772be7d22fSVladimir Kondratiev } 7782be7d22fSVladimir Kondratiev /* 0-based channel indexes */ 7792be7d22fSVladimir Kondratiev cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; 7807743882dSVladimir Kondratiev wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch, 7812be7d22fSVladimir Kondratiev request->channels[i]->center_freq); 7822be7d22fSVladimir Kondratiev } 7832be7d22fSVladimir Kondratiev 78477c91295SVladimir Kondratiev if (request->ie_len) 7855eb443e9SDedy Lansky wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1, 7865eb443e9SDedy Lansky request->ie, request->ie_len, true); 78777c91295SVladimir Kondratiev else 78877c91295SVladimir Kondratiev wil_dbg_misc(wil, "Scan has no IE's\n"); 78977c91295SVladimir Kondratiev 790e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, 791e00243faSLior David request->ie_len, request->ie); 7925421bf0cSVladimir Kondratiev if (rc) 793bb6743f7SLior David goto out_restore; 79477c91295SVladimir Kondratiev 79574997a53SLior David if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) { 79674997a53SLior David cmd.cmd.discovery_mode = 1; 79774997a53SLior David wil_dbg_misc(wil, "active scan with discovery_mode=1\n"); 79874997a53SLior David } 79974997a53SLior David 800e00243faSLior David if (vif->mid == 0) 8014332cac1SLior David wil->radio_wdev = wdev; 802e00243faSLior David rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid, 803e00243faSLior David &cmd, sizeof(cmd.cmd) + 8042be7d22fSVladimir Kondratiev cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); 805ed6f9dc6SVladimir Kondratiev 806bb6743f7SLior David out_restore: 807a2142086SVladimir Kondratiev if (rc) { 808e00243faSLior David del_timer_sync(&vif->scan_timer); 809e00243faSLior David if (vif->mid == 0) 810e00243faSLior David wil->radio_wdev = wil->main_ndev->ieee80211_ptr; 811e00243faSLior David vif->scan_request = NULL; 812a2142086SVladimir Kondratiev } 813bb6743f7SLior David out: 814bb6743f7SLior David mutex_unlock(&wil->mutex); 815ed6f9dc6SVladimir Kondratiev return rc; 8162be7d22fSVladimir Kondratiev } 8172be7d22fSVladimir Kondratiev 818035859a5SMaya Erez static void wil_cfg80211_abort_scan(struct wiphy *wiphy, 819035859a5SMaya Erez struct wireless_dev *wdev) 820035859a5SMaya Erez { 821035859a5SMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy); 822e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 823035859a5SMaya Erez 824035859a5SMaya Erez wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype); 825035859a5SMaya Erez 826035859a5SMaya Erez mutex_lock(&wil->mutex); 827404bbb3cSLior David mutex_lock(&wil->vif_mutex); 828035859a5SMaya Erez 829e00243faSLior David if (!vif->scan_request) 830035859a5SMaya Erez goto out; 831035859a5SMaya Erez 832e00243faSLior David if (wdev != vif->scan_request->wdev) { 833035859a5SMaya Erez wil_dbg_misc(wil, "abort scan was called on the wrong iface\n"); 834035859a5SMaya Erez goto out; 835035859a5SMaya Erez } 836035859a5SMaya Erez 837e00243faSLior David if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev) 838035859a5SMaya Erez wil_p2p_stop_radio_operations(wil); 839035859a5SMaya Erez else 840e00243faSLior David wil_abort_scan(vif, true); 841035859a5SMaya Erez 842035859a5SMaya Erez out: 843404bbb3cSLior David mutex_unlock(&wil->vif_mutex); 844035859a5SMaya Erez mutex_unlock(&wil->mutex); 845035859a5SMaya Erez } 846035859a5SMaya Erez 847feeac225SVladimir Kondratiev static void wil_print_crypto(struct wil6210_priv *wil, 848feeac225SVladimir Kondratiev struct cfg80211_crypto_settings *c) 849feeac225SVladimir Kondratiev { 850feeac225SVladimir Kondratiev int i, n; 851feeac225SVladimir Kondratiev 852feeac225SVladimir Kondratiev wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n", 853feeac225SVladimir Kondratiev c->wpa_versions, c->cipher_group); 854feeac225SVladimir Kondratiev wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise); 855feeac225SVladimir Kondratiev n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise)); 856feeac225SVladimir Kondratiev for (i = 0; i < n; i++) 857feeac225SVladimir Kondratiev wil_dbg_misc(wil, " [%d] = 0x%08x\n", i, 858feeac225SVladimir Kondratiev c->ciphers_pairwise[i]); 859feeac225SVladimir Kondratiev wil_dbg_misc(wil, "}\n"); 860feeac225SVladimir Kondratiev wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites); 861feeac225SVladimir Kondratiev n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites)); 862feeac225SVladimir Kondratiev for (i = 0; i < n; i++) 863feeac225SVladimir Kondratiev wil_dbg_misc(wil, " [%d] = 0x%08x\n", i, 864feeac225SVladimir Kondratiev c->akm_suites[i]); 865feeac225SVladimir Kondratiev wil_dbg_misc(wil, "}\n"); 866feeac225SVladimir Kondratiev wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n", 867feeac225SVladimir Kondratiev c->control_port, be16_to_cpu(c->control_port_ethertype), 868feeac225SVladimir Kondratiev c->control_port_no_encrypt); 869feeac225SVladimir Kondratiev } 870feeac225SVladimir Kondratiev 8718ca26163SVladimir Kondratiev static void wil_print_connect_params(struct wil6210_priv *wil, 8728ca26163SVladimir Kondratiev struct cfg80211_connect_params *sme) 8738ca26163SVladimir Kondratiev { 8748ca26163SVladimir Kondratiev wil_info(wil, "Connecting to:\n"); 8758ca26163SVladimir Kondratiev if (sme->channel) { 8768ca26163SVladimir Kondratiev wil_info(wil, " Channel: %d freq %d\n", 8778ca26163SVladimir Kondratiev sme->channel->hw_value, sme->channel->center_freq); 8788ca26163SVladimir Kondratiev } 8798ca26163SVladimir Kondratiev if (sme->bssid) 8808ca26163SVladimir Kondratiev wil_info(wil, " BSSID: %pM\n", sme->bssid); 8818ca26163SVladimir Kondratiev if (sme->ssid) 8828ca26163SVladimir Kondratiev print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET, 8838ca26163SVladimir Kondratiev 16, 1, sme->ssid, sme->ssid_len, true); 8848ca26163SVladimir Kondratiev wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open"); 885eabb03b4SLior David wil_info(wil, " PBSS: %d\n", sme->pbss); 886feeac225SVladimir Kondratiev wil_print_crypto(wil, &sme->crypto); 8878ca26163SVladimir Kondratiev } 8888ca26163SVladimir Kondratiev 8892be7d22fSVladimir Kondratiev static int wil_cfg80211_connect(struct wiphy *wiphy, 8902be7d22fSVladimir Kondratiev struct net_device *ndev, 8912be7d22fSVladimir Kondratiev struct cfg80211_connect_params *sme) 8922be7d22fSVladimir Kondratiev { 8932be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 894e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 8952be7d22fSVladimir Kondratiev struct cfg80211_bss *bss; 8962be7d22fSVladimir Kondratiev struct wmi_connect_cmd conn; 8972be7d22fSVladimir Kondratiev const u8 *ssid_eid; 8982be7d22fSVladimir Kondratiev const u8 *rsn_eid; 8992be7d22fSVladimir Kondratiev int ch; 9002be7d22fSVladimir Kondratiev int rc = 0; 901eabb03b4SLior David enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS; 9022be7d22fSVladimir Kondratiev 903e00243faSLior David wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid); 904344a7024SVladimir Kondratiev wil_print_connect_params(wil, sme); 905344a7024SVladimir Kondratiev 9065bd60982SLior David if (test_bit(wil_vif_fwconnecting, vif->status) || 9075bd60982SLior David test_bit(wil_vif_fwconnected, vif->status)) 9084cd9e837SVladimir Kondratiev return -EALREADY; 9094cd9e837SVladimir Kondratiev 910344a7024SVladimir Kondratiev if (sme->ie_len > WMI_MAX_IE_LEN) { 911344a7024SVladimir Kondratiev wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len); 912344a7024SVladimir Kondratiev return -ERANGE; 913344a7024SVladimir Kondratiev } 914344a7024SVladimir Kondratiev 915344a7024SVladimir Kondratiev rsn_eid = sme->ie ? 916344a7024SVladimir Kondratiev cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : 917344a7024SVladimir Kondratiev NULL; 91827aa6b71SVladimir Kondratiev if (sme->privacy && !rsn_eid) 91927aa6b71SVladimir Kondratiev wil_info(wil, "WSC connection\n"); 9208ca26163SVladimir Kondratiev 921eabb03b4SLior David if (sme->pbss) 922eabb03b4SLior David bss_type = IEEE80211_BSS_TYPE_PBSS; 92334d50519SLior David 9242be7d22fSVladimir Kondratiev bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, 9252be7d22fSVladimir Kondratiev sme->ssid, sme->ssid_len, 926eabb03b4SLior David bss_type, IEEE80211_PRIVACY_ANY); 9272be7d22fSVladimir Kondratiev if (!bss) { 9282be7d22fSVladimir Kondratiev wil_err(wil, "Unable to find BSS\n"); 9292be7d22fSVladimir Kondratiev return -ENOENT; 9302be7d22fSVladimir Kondratiev } 9312be7d22fSVladimir Kondratiev 9322be7d22fSVladimir Kondratiev ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); 9332be7d22fSVladimir Kondratiev if (!ssid_eid) { 9342be7d22fSVladimir Kondratiev wil_err(wil, "No SSID\n"); 9352be7d22fSVladimir Kondratiev rc = -ENOENT; 9362be7d22fSVladimir Kondratiev goto out; 9372be7d22fSVladimir Kondratiev } 938e00243faSLior David vif->privacy = sme->privacy; 939e00243faSLior David vif->pbss = sme->pbss; 9402be7d22fSVladimir Kondratiev 941e00243faSLior David if (vif->privacy) { 942230d8442SVladimir Kondratiev /* For secure assoc, remove old keys */ 943e00243faSLior David rc = wmi_del_cipher_key(vif, 0, bss->bssid, 944230d8442SVladimir Kondratiev WMI_KEY_USE_PAIRWISE); 9452be7d22fSVladimir Kondratiev if (rc) { 946230d8442SVladimir Kondratiev wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n"); 947230d8442SVladimir Kondratiev goto out; 948230d8442SVladimir Kondratiev } 949e00243faSLior David rc = wmi_del_cipher_key(vif, 0, bss->bssid, 950230d8442SVladimir Kondratiev WMI_KEY_USE_RX_GROUP); 951230d8442SVladimir Kondratiev if (rc) { 952230d8442SVladimir Kondratiev wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n"); 9532be7d22fSVladimir Kondratiev goto out; 9542be7d22fSVladimir Kondratiev } 955ac4acdb7SVladimir Kondratiev } 956ac4acdb7SVladimir Kondratiev 957ac4acdb7SVladimir Kondratiev /* WMI_SET_APPIE_CMD. ie may contain rsn info as well as other info 958ac4acdb7SVladimir Kondratiev * elements. Send it also in case it's empty, to erase previously set 959ac4acdb7SVladimir Kondratiev * ies in FW. 960ac4acdb7SVladimir Kondratiev */ 961e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); 9625421bf0cSVladimir Kondratiev if (rc) 9632be7d22fSVladimir Kondratiev goto out; 9642be7d22fSVladimir Kondratiev 9652be7d22fSVladimir Kondratiev /* WMI_CONNECT_CMD */ 9662be7d22fSVladimir Kondratiev memset(&conn, 0, sizeof(conn)); 967acc9780dSVladimir Kondratiev switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) { 9682be7d22fSVladimir Kondratiev case WLAN_CAPABILITY_DMG_TYPE_AP: 9692be7d22fSVladimir Kondratiev conn.network_type = WMI_NETTYPE_INFRA; 9702be7d22fSVladimir Kondratiev break; 9712be7d22fSVladimir Kondratiev case WLAN_CAPABILITY_DMG_TYPE_PBSS: 9722be7d22fSVladimir Kondratiev conn.network_type = WMI_NETTYPE_P2P; 9732be7d22fSVladimir Kondratiev break; 9742be7d22fSVladimir Kondratiev default: 9752be7d22fSVladimir Kondratiev wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", 9762be7d22fSVladimir Kondratiev bss->capability); 9772be7d22fSVladimir Kondratiev goto out; 9782be7d22fSVladimir Kondratiev } 979e00243faSLior David if (vif->privacy) { 98027aa6b71SVladimir Kondratiev if (rsn_eid) { /* regular secure connection */ 9812be7d22fSVladimir Kondratiev conn.dot11_auth_mode = WMI_AUTH11_SHARED; 9822be7d22fSVladimir Kondratiev conn.auth_mode = WMI_AUTH_WPA2_PSK; 9832be7d22fSVladimir Kondratiev conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; 9842be7d22fSVladimir Kondratiev conn.pairwise_crypto_len = 16; 985230d8442SVladimir Kondratiev conn.group_crypto_type = WMI_CRYPT_AES_GCMP; 986230d8442SVladimir Kondratiev conn.group_crypto_len = 16; 98727aa6b71SVladimir Kondratiev } else { /* WSC */ 98827aa6b71SVladimir Kondratiev conn.dot11_auth_mode = WMI_AUTH11_WSC; 98927aa6b71SVladimir Kondratiev conn.auth_mode = WMI_AUTH_NONE; 99027aa6b71SVladimir Kondratiev } 99127aa6b71SVladimir Kondratiev } else { /* insecure connection */ 9922be7d22fSVladimir Kondratiev conn.dot11_auth_mode = WMI_AUTH11_OPEN; 9932be7d22fSVladimir Kondratiev conn.auth_mode = WMI_AUTH_NONE; 9942be7d22fSVladimir Kondratiev } 9952be7d22fSVladimir Kondratiev 9962be7d22fSVladimir Kondratiev conn.ssid_len = min_t(u8, ssid_eid[1], 32); 9972be7d22fSVladimir Kondratiev memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); 9982be7d22fSVladimir Kondratiev 9992be7d22fSVladimir Kondratiev ch = bss->channel->hw_value; 10002be7d22fSVladimir Kondratiev if (ch == 0) { 10012be7d22fSVladimir Kondratiev wil_err(wil, "BSS at unknown frequency %dMhz\n", 10022be7d22fSVladimir Kondratiev bss->channel->center_freq); 10032be7d22fSVladimir Kondratiev rc = -EOPNOTSUPP; 10042be7d22fSVladimir Kondratiev goto out; 10052be7d22fSVladimir Kondratiev } 10062be7d22fSVladimir Kondratiev conn.channel = ch - 1; 10072be7d22fSVladimir Kondratiev 1008a82553bbSVladimir Kondratiev ether_addr_copy(conn.bssid, bss->bssid); 1009a82553bbSVladimir Kondratiev ether_addr_copy(conn.dst_mac, bss->bssid); 1010e83eb2fcSVladimir Kondratiev 10115bd60982SLior David set_bit(wil_vif_fwconnecting, vif->status); 10122be7d22fSVladimir Kondratiev 1013e00243faSLior David rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn)); 10142be7d22fSVladimir Kondratiev if (rc == 0) { 1015c5e96c91SDedy Lansky netif_carrier_on(ndev); 10165bd60982SLior David if (!wil_has_other_active_ifaces(wil, ndev, false, true)) 10179953a782SLior David wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); 1018e00243faSLior David vif->bss = bss; 10192be7d22fSVladimir Kondratiev /* Connect can take lots of time */ 1020e00243faSLior David mod_timer(&vif->connect_timer, 1021d83ad4c4SLior David jiffies + msecs_to_jiffies(5000)); 1022b338f74eSVladimir Kondratiev } else { 10235bd60982SLior David clear_bit(wil_vif_fwconnecting, vif->status); 10242be7d22fSVladimir Kondratiev } 10252be7d22fSVladimir Kondratiev 10262be7d22fSVladimir Kondratiev out: 10275b112d3dSJohannes Berg cfg80211_put_bss(wiphy, bss); 10282be7d22fSVladimir Kondratiev 10292be7d22fSVladimir Kondratiev return rc; 10302be7d22fSVladimir Kondratiev } 10312be7d22fSVladimir Kondratiev 10322be7d22fSVladimir Kondratiev static int wil_cfg80211_disconnect(struct wiphy *wiphy, 10332be7d22fSVladimir Kondratiev struct net_device *ndev, 10342be7d22fSVladimir Kondratiev u16 reason_code) 10352be7d22fSVladimir Kondratiev { 10362be7d22fSVladimir Kondratiev int rc; 10372be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1038e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 10392be7d22fSVladimir Kondratiev 1040e00243faSLior David wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n", 1041e00243faSLior David reason_code, vif->mid); 1042de9084efSVladimir Kondratiev 10435bd60982SLior David if (!(test_bit(wil_vif_fwconnecting, vif->status) || 10445bd60982SLior David test_bit(wil_vif_fwconnected, vif->status))) { 1045af3db60aSLazar Alexei wil_err(wil, "Disconnect was called while disconnected\n"); 104678771d76SVladimir Kondratiev return 0; 104778771d76SVladimir Kondratiev } 104878771d76SVladimir Kondratiev 1049e00243faSLior David vif->locally_generated_disc = true; 1050e00243faSLior David rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0, 105178771d76SVladimir Kondratiev WMI_DISCONNECT_EVENTID, NULL, 0, 105278771d76SVladimir Kondratiev WIL6210_DISCONNECT_TO_MS); 105378771d76SVladimir Kondratiev if (rc) 1054af3db60aSLazar Alexei wil_err(wil, "disconnect error %d\n", rc); 10552be7d22fSVladimir Kondratiev 10562be7d22fSVladimir Kondratiev return rc; 10572be7d22fSVladimir Kondratiev } 10582be7d22fSVladimir Kondratiev 10593fea18d0SLior David static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) 10603fea18d0SLior David { 10613fea18d0SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy); 10623fea18d0SLior David int rc; 10633fea18d0SLior David 10643fea18d0SLior David /* these parameters are explicitly not supported */ 10653fea18d0SLior David if (changed & (WIPHY_PARAM_RETRY_LONG | 10663fea18d0SLior David WIPHY_PARAM_FRAG_THRESHOLD | 10673fea18d0SLior David WIPHY_PARAM_RTS_THRESHOLD)) 10683fea18d0SLior David return -ENOTSUPP; 10693fea18d0SLior David 10703fea18d0SLior David if (changed & WIPHY_PARAM_RETRY_SHORT) { 10713fea18d0SLior David rc = wmi_set_mgmt_retry(wil, wiphy->retry_short); 10723fea18d0SLior David if (rc) 10733fea18d0SLior David return rc; 10743fea18d0SLior David } 10753fea18d0SLior David 10763fea18d0SLior David return 0; 10773fea18d0SLior David } 10783fea18d0SLior David 10790b39aaf2SVladimir Kondratiev int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 10801647f12fSVladimir Kondratiev struct cfg80211_mgmt_tx_params *params, 10811647f12fSVladimir Kondratiev u64 *cookie) 10821647f12fSVladimir Kondratiev { 10831647f12fSVladimir Kondratiev const u8 *buf = params->buf; 108451a58709SLior David size_t len = params->len, total; 10851647f12fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1086e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 10871647f12fSVladimir Kondratiev int rc; 1088304464f4SVladimir Kondratiev bool tx_status = false; 10891647f12fSVladimir Kondratiev struct ieee80211_mgmt *mgmt_frame = (void *)buf; 10901647f12fSVladimir Kondratiev struct wmi_sw_tx_req_cmd *cmd; 10911647f12fSVladimir Kondratiev struct { 1092b874ddecSLior David struct wmi_cmd_hdr wmi; 10931647f12fSVladimir Kondratiev struct wmi_sw_tx_complete_event evt; 10941647f12fSVladimir Kondratiev } __packed evt; 10951647f12fSVladimir Kondratiev 1096e6d68341SDedy Lansky /* Note, currently we do not support the "wait" parameter, user-space 1097e6d68341SDedy Lansky * must call remain_on_channel before mgmt_tx or listen on a channel 1098e6d68341SDedy Lansky * another way (AP/PCP or connected station) 1099e6d68341SDedy Lansky * in addition we need to check if specified "chan" argument is 1100e6d68341SDedy Lansky * different from currently "listened" channel and fail if it is. 1101e6d68341SDedy Lansky */ 1102e6d68341SDedy Lansky 1103e00243faSLior David wil_dbg_misc(wil, "mgmt_tx mid %d\n", vif->mid); 11045eb443e9SDedy Lansky wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, 11055eb443e9SDedy Lansky len, true); 1106e6d68341SDedy Lansky 11076641525cSHamad Kadmany if (len < sizeof(struct ieee80211_hdr_3addr)) 11086641525cSHamad Kadmany return -EINVAL; 11096641525cSHamad Kadmany 111051a58709SLior David total = sizeof(*cmd) + len; 111151a58709SLior David if (total < len) 111251a58709SLior David return -EINVAL; 111351a58709SLior David 111451a58709SLior David cmd = kmalloc(total, GFP_KERNEL); 1115304464f4SVladimir Kondratiev if (!cmd) { 1116304464f4SVladimir Kondratiev rc = -ENOMEM; 1117304464f4SVladimir Kondratiev goto out; 1118304464f4SVladimir Kondratiev } 11191647f12fSVladimir Kondratiev 11201647f12fSVladimir Kondratiev memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); 11211647f12fSVladimir Kondratiev cmd->len = cpu_to_le16(len); 11221647f12fSVladimir Kondratiev memcpy(cmd->payload, buf, len); 11231647f12fSVladimir Kondratiev 1124e00243faSLior David rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, vif->mid, cmd, total, 11251647f12fSVladimir Kondratiev WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); 11261647f12fSVladimir Kondratiev if (rc == 0) 1127304464f4SVladimir Kondratiev tx_status = !evt.evt.status; 11281647f12fSVladimir Kondratiev 11291647f12fSVladimir Kondratiev kfree(cmd); 1130304464f4SVladimir Kondratiev out: 1131304464f4SVladimir Kondratiev cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, 1132304464f4SVladimir Kondratiev tx_status, GFP_KERNEL); 11331647f12fSVladimir Kondratiev return rc; 11341647f12fSVladimir Kondratiev } 11351647f12fSVladimir Kondratiev 11362be7d22fSVladimir Kondratiev static int wil_cfg80211_set_channel(struct wiphy *wiphy, 11372be7d22fSVladimir Kondratiev struct cfg80211_chan_def *chandef) 11382be7d22fSVladimir Kondratiev { 11392be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 11402be7d22fSVladimir Kondratiev 11417d3e4dbeSLior David wil->monitor_chandef = *chandef; 11422be7d22fSVladimir Kondratiev 11432be7d22fSVladimir Kondratiev return 0; 11442be7d22fSVladimir Kondratiev } 11452be7d22fSVladimir Kondratiev 1146e00243faSLior David static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev, 1147230d8442SVladimir Kondratiev bool pairwise) 1148230d8442SVladimir Kondratiev { 1149e00243faSLior David struct wil6210_priv *wil = wdev_to_wil(wdev); 1150230d8442SVladimir Kondratiev enum wmi_key_usage rc; 1151230d8442SVladimir Kondratiev 1152230d8442SVladimir Kondratiev if (pairwise) { 1153230d8442SVladimir Kondratiev rc = WMI_KEY_USE_PAIRWISE; 1154230d8442SVladimir Kondratiev } else { 1155230d8442SVladimir Kondratiev switch (wdev->iftype) { 1156230d8442SVladimir Kondratiev case NL80211_IFTYPE_STATION: 1157e6d68341SDedy Lansky case NL80211_IFTYPE_P2P_CLIENT: 1158230d8442SVladimir Kondratiev rc = WMI_KEY_USE_RX_GROUP; 1159230d8442SVladimir Kondratiev break; 1160230d8442SVladimir Kondratiev case NL80211_IFTYPE_AP: 1161e6d68341SDedy Lansky case NL80211_IFTYPE_P2P_GO: 1162230d8442SVladimir Kondratiev rc = WMI_KEY_USE_TX_GROUP; 1163230d8442SVladimir Kondratiev break; 1164230d8442SVladimir Kondratiev default: 1165230d8442SVladimir Kondratiev /* TODO: Rx GTK or Tx GTK? */ 1166230d8442SVladimir Kondratiev wil_err(wil, "Can't determine GTK type\n"); 1167230d8442SVladimir Kondratiev rc = WMI_KEY_USE_RX_GROUP; 1168230d8442SVladimir Kondratiev break; 1169230d8442SVladimir Kondratiev } 1170230d8442SVladimir Kondratiev } 1171af3db60aSLazar Alexei wil_dbg_misc(wil, "detect_key_usage: -> %s\n", key_usage_str[rc]); 1172230d8442SVladimir Kondratiev 1173230d8442SVladimir Kondratiev return rc; 1174230d8442SVladimir Kondratiev } 1175230d8442SVladimir Kondratiev 117674b6ac58SMaya Erez static struct wil_sta_info * 1177e00243faSLior David wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid, 117858527421SVladimir Kondratiev enum wmi_key_usage key_usage, const u8 *mac_addr) 117958527421SVladimir Kondratiev { 118058527421SVladimir Kondratiev int cid = -EINVAL; 118158527421SVladimir Kondratiev 118258527421SVladimir Kondratiev if (key_usage == WMI_KEY_USE_TX_GROUP) 118358527421SVladimir Kondratiev return NULL; /* not needed */ 118458527421SVladimir Kondratiev 118558527421SVladimir Kondratiev /* supplicant provides Rx group key in STA mode with NULL MAC address */ 118658527421SVladimir Kondratiev if (mac_addr) 1187e00243faSLior David cid = wil_find_cid(wil, mid, mac_addr); 118858527421SVladimir Kondratiev else if (key_usage == WMI_KEY_USE_RX_GROUP) 1189e00243faSLior David cid = wil_find_cid_by_idx(wil, mid, 0); 119058527421SVladimir Kondratiev if (cid < 0) { 119174b6ac58SMaya Erez wil_err(wil, "No CID for %pM %s\n", mac_addr, 119274b6ac58SMaya Erez key_usage_str[key_usage]); 119358527421SVladimir Kondratiev return ERR_PTR(cid); 119458527421SVladimir Kondratiev } 119558527421SVladimir Kondratiev 119674b6ac58SMaya Erez return &wil->sta[cid]; 119774b6ac58SMaya Erez } 119858527421SVladimir Kondratiev 119974b6ac58SMaya Erez static void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage, 120074b6ac58SMaya Erez struct wil_sta_info *cs, 120174b6ac58SMaya Erez struct key_params *params) 120274b6ac58SMaya Erez { 120374b6ac58SMaya Erez struct wil_tid_crypto_rx_single *cc; 120474b6ac58SMaya Erez int tid; 120574b6ac58SMaya Erez 120674b6ac58SMaya Erez if (!cs) 120774b6ac58SMaya Erez return; 120874b6ac58SMaya Erez 120974b6ac58SMaya Erez switch (key_usage) { 121074b6ac58SMaya Erez case WMI_KEY_USE_PAIRWISE: 121174b6ac58SMaya Erez for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { 121274b6ac58SMaya Erez cc = &cs->tid_crypto_rx[tid].key_id[key_index]; 121374b6ac58SMaya Erez if (params->seq) 121474b6ac58SMaya Erez memcpy(cc->pn, params->seq, 121574b6ac58SMaya Erez IEEE80211_GCMP_PN_LEN); 121674b6ac58SMaya Erez else 121774b6ac58SMaya Erez memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN); 121874b6ac58SMaya Erez cc->key_set = true; 121974b6ac58SMaya Erez } 122074b6ac58SMaya Erez break; 122174b6ac58SMaya Erez case WMI_KEY_USE_RX_GROUP: 122274b6ac58SMaya Erez cc = &cs->group_crypto_rx.key_id[key_index]; 122374b6ac58SMaya Erez if (params->seq) 122474b6ac58SMaya Erez memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN); 122574b6ac58SMaya Erez else 122674b6ac58SMaya Erez memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN); 122774b6ac58SMaya Erez cc->key_set = true; 122874b6ac58SMaya Erez break; 122974b6ac58SMaya Erez default: 123074b6ac58SMaya Erez break; 123174b6ac58SMaya Erez } 123274b6ac58SMaya Erez } 123374b6ac58SMaya Erez 123474b6ac58SMaya Erez static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage, 123574b6ac58SMaya Erez struct wil_sta_info *cs) 123674b6ac58SMaya Erez { 123774b6ac58SMaya Erez struct wil_tid_crypto_rx_single *cc; 123874b6ac58SMaya Erez int tid; 123974b6ac58SMaya Erez 124074b6ac58SMaya Erez if (!cs) 124174b6ac58SMaya Erez return; 124274b6ac58SMaya Erez 124374b6ac58SMaya Erez switch (key_usage) { 124474b6ac58SMaya Erez case WMI_KEY_USE_PAIRWISE: 124574b6ac58SMaya Erez for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { 124674b6ac58SMaya Erez cc = &cs->tid_crypto_rx[tid].key_id[key_index]; 124774b6ac58SMaya Erez cc->key_set = false; 124874b6ac58SMaya Erez } 124974b6ac58SMaya Erez break; 125074b6ac58SMaya Erez case WMI_KEY_USE_RX_GROUP: 125174b6ac58SMaya Erez cc = &cs->group_crypto_rx.key_id[key_index]; 125274b6ac58SMaya Erez cc->key_set = false; 125374b6ac58SMaya Erez break; 125474b6ac58SMaya Erez default: 125574b6ac58SMaya Erez break; 125674b6ac58SMaya Erez } 125758527421SVladimir Kondratiev } 125858527421SVladimir Kondratiev 12592be7d22fSVladimir Kondratiev static int wil_cfg80211_add_key(struct wiphy *wiphy, 12602be7d22fSVladimir Kondratiev struct net_device *ndev, 12612be7d22fSVladimir Kondratiev u8 key_index, bool pairwise, 12622be7d22fSVladimir Kondratiev const u8 *mac_addr, 12632be7d22fSVladimir Kondratiev struct key_params *params) 12642be7d22fSVladimir Kondratiev { 126558527421SVladimir Kondratiev int rc; 1266e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 12672be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1268e00243faSLior David struct wireless_dev *wdev = vif_to_wdev(vif); 1269e00243faSLior David enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise); 1270e00243faSLior David struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid, 1271e00243faSLior David key_usage, 127258527421SVladimir Kondratiev mac_addr); 12732be7d22fSVladimir Kondratiev 127474b6ac58SMaya Erez if (!params) { 127574b6ac58SMaya Erez wil_err(wil, "NULL params\n"); 127674b6ac58SMaya Erez return -EINVAL; 127774b6ac58SMaya Erez } 127874b6ac58SMaya Erez 1279af3db60aSLazar Alexei wil_dbg_misc(wil, "add_key: %pM %s[%d] PN %*phN\n", 128058527421SVladimir Kondratiev mac_addr, key_usage_str[key_usage], key_index, 128158527421SVladimir Kondratiev params->seq_len, params->seq); 1282db8adcbfSVladimir Kondratiev 128374b6ac58SMaya Erez if (IS_ERR(cs)) { 1284af3db60aSLazar Alexei wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n", 1285af3db60aSLazar Alexei mac_addr, key_usage_str[key_usage], key_index, 128658527421SVladimir Kondratiev params->seq_len, params->seq); 128758527421SVladimir Kondratiev return -EINVAL; 128858527421SVladimir Kondratiev } 128958527421SVladimir Kondratiev 129074b6ac58SMaya Erez wil_del_rx_key(key_index, key_usage, cs); 129158527421SVladimir Kondratiev 129258527421SVladimir Kondratiev if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) { 129358527421SVladimir Kondratiev wil_err(wil, 1294af3db60aSLazar Alexei "Wrong PN len %d, %pM %s[%d] PN %*phN\n", 1295af3db60aSLazar Alexei params->seq_len, mac_addr, 129658527421SVladimir Kondratiev key_usage_str[key_usage], key_index, 129758527421SVladimir Kondratiev params->seq_len, params->seq); 129858527421SVladimir Kondratiev return -EINVAL; 129958527421SVladimir Kondratiev } 130058527421SVladimir Kondratiev 1301e00243faSLior David rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len, 1302230d8442SVladimir Kondratiev params->key, key_usage); 130374b6ac58SMaya Erez if (!rc) 130474b6ac58SMaya Erez wil_set_crypto_rx(key_index, key_usage, cs, params); 130558527421SVladimir Kondratiev 130658527421SVladimir Kondratiev return rc; 13072be7d22fSVladimir Kondratiev } 13082be7d22fSVladimir Kondratiev 13092be7d22fSVladimir Kondratiev static int wil_cfg80211_del_key(struct wiphy *wiphy, 13102be7d22fSVladimir Kondratiev struct net_device *ndev, 13112be7d22fSVladimir Kondratiev u8 key_index, bool pairwise, 13122be7d22fSVladimir Kondratiev const u8 *mac_addr) 13132be7d22fSVladimir Kondratiev { 1314e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 13152be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1316e00243faSLior David struct wireless_dev *wdev = vif_to_wdev(vif); 1317e00243faSLior David enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise); 1318e00243faSLior David struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid, 1319e00243faSLior David key_usage, 132058527421SVladimir Kondratiev mac_addr); 13212be7d22fSVladimir Kondratiev 1322af3db60aSLazar Alexei wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr, 132358527421SVladimir Kondratiev key_usage_str[key_usage], key_index); 132458527421SVladimir Kondratiev 132574b6ac58SMaya Erez if (IS_ERR(cs)) 1326af3db60aSLazar Alexei wil_info(wil, "Not connected, %pM %s[%d]\n", 132758527421SVladimir Kondratiev mac_addr, key_usage_str[key_usage], key_index); 132858527421SVladimir Kondratiev 132974b6ac58SMaya Erez if (!IS_ERR_OR_NULL(cs)) 133074b6ac58SMaya Erez wil_del_rx_key(key_index, key_usage, cs); 1331db8adcbfSVladimir Kondratiev 1332e00243faSLior David return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage); 13332be7d22fSVladimir Kondratiev } 13342be7d22fSVladimir Kondratiev 13352be7d22fSVladimir Kondratiev /* Need to be present or wiphy_new() will WARN */ 13362be7d22fSVladimir Kondratiev static int wil_cfg80211_set_default_key(struct wiphy *wiphy, 13372be7d22fSVladimir Kondratiev struct net_device *ndev, 13382be7d22fSVladimir Kondratiev u8 key_index, bool unicast, 13392be7d22fSVladimir Kondratiev bool multicast) 13402be7d22fSVladimir Kondratiev { 1341280ab987SLior David struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1342280ab987SLior David 1343af3db60aSLazar Alexei wil_dbg_misc(wil, "set_default_key: entered\n"); 13442be7d22fSVladimir Kondratiev return 0; 13452be7d22fSVladimir Kondratiev } 13462be7d22fSVladimir Kondratiev 13471647f12fSVladimir Kondratiev static int wil_remain_on_channel(struct wiphy *wiphy, 13481647f12fSVladimir Kondratiev struct wireless_dev *wdev, 13491647f12fSVladimir Kondratiev struct ieee80211_channel *chan, 13501647f12fSVladimir Kondratiev unsigned int duration, 13511647f12fSVladimir Kondratiev u64 *cookie) 13521647f12fSVladimir Kondratiev { 13531647f12fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 13541647f12fSVladimir Kondratiev int rc; 13551647f12fSVladimir Kondratiev 1356af3db60aSLazar Alexei wil_dbg_misc(wil, 1357af3db60aSLazar Alexei "remain_on_channel: center_freq=%d, duration=%d iftype=%d\n", 1358af3db60aSLazar Alexei chan->center_freq, duration, wdev->iftype); 13591647f12fSVladimir Kondratiev 1360bb6743f7SLior David rc = wil_p2p_listen(wil, wdev, duration, chan, cookie); 13611647f12fSVladimir Kondratiev return rc; 13621647f12fSVladimir Kondratiev } 13631647f12fSVladimir Kondratiev 13641647f12fSVladimir Kondratiev static int wil_cancel_remain_on_channel(struct wiphy *wiphy, 13651647f12fSVladimir Kondratiev struct wireless_dev *wdev, 13661647f12fSVladimir Kondratiev u64 cookie) 13671647f12fSVladimir Kondratiev { 13681647f12fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1369e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 13701647f12fSVladimir Kondratiev 1371af3db60aSLazar Alexei wil_dbg_misc(wil, "cancel_remain_on_channel\n"); 13721647f12fSVladimir Kondratiev 1373e00243faSLior David return wil_p2p_cancel_listen(vif, cookie); 13741647f12fSVladimir Kondratiev } 13751647f12fSVladimir Kondratiev 1376c100c883SLior David /** 1377c100c883SLior David * find a specific IE in a list of IEs 1378c100c883SLior David * return a pointer to the beginning of IE in the list 1379c100c883SLior David * or NULL if not found 1380c100c883SLior David */ 1381c100c883SLior David static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie, 1382c100c883SLior David u16 ie_len) 1383c100c883SLior David { 1384c100c883SLior David struct ieee80211_vendor_ie *vie; 1385c100c883SLior David u32 oui; 1386c100c883SLior David 1387c100c883SLior David /* IE tag at offset 0, length at offset 1 */ 1388c100c883SLior David if (ie_len < 2 || 2 + ie[1] > ie_len) 1389c100c883SLior David return NULL; 1390c100c883SLior David 1391c100c883SLior David if (ie[0] != WLAN_EID_VENDOR_SPECIFIC) 1392c100c883SLior David return cfg80211_find_ie(ie[0], ies, ies_len); 1393c100c883SLior David 1394c100c883SLior David /* make sure there is room for 3 bytes OUI + 1 byte OUI type */ 1395c100c883SLior David if (ie[1] < 4) 1396c100c883SLior David return NULL; 1397c100c883SLior David vie = (struct ieee80211_vendor_ie *)ie; 1398c100c883SLior David oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2]; 1399c100c883SLior David return cfg80211_find_vendor_ie(oui, vie->oui_type, ies, 1400c100c883SLior David ies_len); 1401c100c883SLior David } 1402c100c883SLior David 1403c100c883SLior David /** 1404c100c883SLior David * merge the IEs in two lists into a single list. 1405c100c883SLior David * do not include IEs from the second list which exist in the first list. 1406c100c883SLior David * add only vendor specific IEs from second list to keep 1407c100c883SLior David * the merged list sorted (since vendor-specific IE has the 1408c100c883SLior David * highest tag number) 1409c100c883SLior David * caller must free the allocated memory for merged IEs 1410c100c883SLior David */ 1411c100c883SLior David static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len, 1412c100c883SLior David const u8 *ies2, u16 ies2_len, 1413c100c883SLior David u8 **merged_ies, u16 *merged_len) 1414c100c883SLior David { 1415c100c883SLior David u8 *buf, *dpos; 1416c100c883SLior David const u8 *spos; 1417c100c883SLior David 1418c100c883SLior David if (ies1_len == 0 && ies2_len == 0) { 1419c100c883SLior David *merged_ies = NULL; 1420c100c883SLior David *merged_len = 0; 1421c100c883SLior David return 0; 1422c100c883SLior David } 1423c100c883SLior David 1424c100c883SLior David buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL); 1425c100c883SLior David if (!buf) 1426c100c883SLior David return -ENOMEM; 1427c100c883SLior David memcpy(buf, ies1, ies1_len); 1428c100c883SLior David dpos = buf + ies1_len; 1429c100c883SLior David spos = ies2; 1430c100c883SLior David while (spos + 1 < ies2 + ies2_len) { 1431c100c883SLior David /* IE tag at offset 0, length at offset 1 */ 1432c100c883SLior David u16 ielen = 2 + spos[1]; 1433c100c883SLior David 1434c100c883SLior David if (spos + ielen > ies2 + ies2_len) 1435c100c883SLior David break; 1436c100c883SLior David if (spos[0] == WLAN_EID_VENDOR_SPECIFIC && 1437c100c883SLior David !_wil_cfg80211_find_ie(ies1, ies1_len, spos, ielen)) { 1438c100c883SLior David memcpy(dpos, spos, ielen); 1439c100c883SLior David dpos += ielen; 1440c100c883SLior David } 1441c100c883SLior David spos += ielen; 1442c100c883SLior David } 1443c100c883SLior David 1444c100c883SLior David *merged_ies = buf; 1445c100c883SLior David *merged_len = dpos - buf; 1446c100c883SLior David return 0; 1447c100c883SLior David } 1448c100c883SLior David 1449ca959773SVladimir Kondratiev static void wil_print_bcon_data(struct cfg80211_beacon_data *b) 1450ca959773SVladimir Kondratiev { 14515eb443e9SDedy Lansky wil_hex_dump_misc("head ", DUMP_PREFIX_OFFSET, 16, 1, 14525eb443e9SDedy Lansky b->head, b->head_len, true); 14535eb443e9SDedy Lansky wil_hex_dump_misc("tail ", DUMP_PREFIX_OFFSET, 16, 1, 14545eb443e9SDedy Lansky b->tail, b->tail_len, true); 14555eb443e9SDedy Lansky wil_hex_dump_misc("BCON IE ", DUMP_PREFIX_OFFSET, 16, 1, 14565eb443e9SDedy Lansky b->beacon_ies, b->beacon_ies_len, true); 14575eb443e9SDedy Lansky wil_hex_dump_misc("PROBE ", DUMP_PREFIX_OFFSET, 16, 1, 14585eb443e9SDedy Lansky b->probe_resp, b->probe_resp_len, true); 14595eb443e9SDedy Lansky wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1, 14605eb443e9SDedy Lansky b->proberesp_ies, b->proberesp_ies_len, true); 14615eb443e9SDedy Lansky wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1, 14625eb443e9SDedy Lansky b->assocresp_ies, b->assocresp_ies_len, true); 1463ca959773SVladimir Kondratiev } 1464ca959773SVladimir Kondratiev 146533190ebfSVladimir Kondratiev /* internal functions for device reset and starting AP */ 1466e00243faSLior David static int _wil_cfg80211_set_ies(struct wil6210_vif *vif, 1467cab5abbfSVladimir Kondratiev struct cfg80211_beacon_data *bcon) 146833190ebfSVladimir Kondratiev { 146933190ebfSVladimir Kondratiev int rc; 1470c100c883SLior David u16 len = 0, proberesp_len = 0; 1471c100c883SLior David u8 *ies = NULL, *proberesp = NULL; 147233190ebfSVladimir Kondratiev 1473c100c883SLior David if (bcon->probe_resp) { 1474c100c883SLior David struct ieee80211_mgmt *f = 1475c100c883SLior David (struct ieee80211_mgmt *)bcon->probe_resp; 1476c100c883SLior David size_t hlen = offsetof(struct ieee80211_mgmt, 1477c100c883SLior David u.probe_resp.variable); 1478c100c883SLior David proberesp = f->u.probe_resp.variable; 1479c100c883SLior David proberesp_len = bcon->probe_resp_len - hlen; 1480c100c883SLior David } 1481c100c883SLior David rc = _wil_cfg80211_merge_extra_ies(proberesp, 1482c100c883SLior David proberesp_len, 1483c100c883SLior David bcon->proberesp_ies, 1484c100c883SLior David bcon->proberesp_ies_len, 1485c100c883SLior David &ies, &len); 1486c100c883SLior David 14875421bf0cSVladimir Kondratiev if (rc) 1488c100c883SLior David goto out; 148933190ebfSVladimir Kondratiev 1490e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies); 1491c100c883SLior David if (rc) 1492c100c883SLior David goto out; 1493c100c883SLior David 1494c100c883SLior David if (bcon->assocresp_ies) 1495e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, 1496c100c883SLior David bcon->assocresp_ies_len, bcon->assocresp_ies); 1497c100c883SLior David else 1498e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies); 1499cab5abbfSVladimir Kondratiev #if 0 /* to use beacon IE's, remove this #if 0 */ 15005421bf0cSVladimir Kondratiev if (rc) 1501c100c883SLior David goto out; 15025421bf0cSVladimir Kondratiev 1503e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_BEACON, 1504e00243faSLior David bcon->tail_len, bcon->tail); 1505cab5abbfSVladimir Kondratiev #endif 1506c100c883SLior David out: 1507c100c883SLior David kfree(ies); 15085421bf0cSVladimir Kondratiev return rc; 150933190ebfSVladimir Kondratiev } 151033190ebfSVladimir Kondratiev 151133190ebfSVladimir Kondratiev static int _wil_cfg80211_start_ap(struct wiphy *wiphy, 151233190ebfSVladimir Kondratiev struct net_device *ndev, 151333190ebfSVladimir Kondratiev const u8 *ssid, size_t ssid_len, u32 privacy, 151433190ebfSVladimir Kondratiev int bi, u8 chan, 1515cab5abbfSVladimir Kondratiev struct cfg80211_beacon_data *bcon, 1516eabb03b4SLior David u8 hidden_ssid, u32 pbss) 151733190ebfSVladimir Kondratiev { 151833190ebfSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1519e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 152033190ebfSVladimir Kondratiev int rc; 152133190ebfSVladimir Kondratiev struct wireless_dev *wdev = ndev->ieee80211_ptr; 152233190ebfSVladimir Kondratiev u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); 1523b4944f2cSLior David u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO); 152433190ebfSVladimir Kondratiev 1525eabb03b4SLior David if (pbss) 1526eabb03b4SLior David wmi_nettype = WMI_NETTYPE_P2P; 1527eabb03b4SLior David 1528e00243faSLior David wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go); 1529b4944f2cSLior David if (is_go && !pbss) { 1530af3db60aSLazar Alexei wil_err(wil, "P2P GO must be in PBSS\n"); 1531b4944f2cSLior David return -ENOTSUPP; 1532b4944f2cSLior David } 1533b4944f2cSLior David 153433190ebfSVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle); 153533190ebfSVladimir Kondratiev 153633190ebfSVladimir Kondratiev mutex_lock(&wil->mutex); 153733190ebfSVladimir Kondratiev 15383ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, true, false)) { 153933190ebfSVladimir Kondratiev __wil_down(wil); 154033190ebfSVladimir Kondratiev rc = __wil_up(wil); 154133190ebfSVladimir Kondratiev if (rc) 154233190ebfSVladimir Kondratiev goto out; 15433ada9314SLior David } 154433190ebfSVladimir Kondratiev 1545e00243faSLior David rc = wmi_set_ssid(vif, ssid_len, ssid); 154633190ebfSVladimir Kondratiev if (rc) 154733190ebfSVladimir Kondratiev goto out; 154833190ebfSVladimir Kondratiev 1549e00243faSLior David rc = _wil_cfg80211_set_ies(vif, bcon); 155033190ebfSVladimir Kondratiev if (rc) 155133190ebfSVladimir Kondratiev goto out; 155233190ebfSVladimir Kondratiev 1553e00243faSLior David vif->privacy = privacy; 1554e00243faSLior David vif->channel = chan; 1555e00243faSLior David vif->hidden_ssid = hidden_ssid; 1556e00243faSLior David vif->pbss = pbss; 155733190ebfSVladimir Kondratiev 155833190ebfSVladimir Kondratiev netif_carrier_on(ndev); 15593ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, false, true)) 15609953a782SLior David wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); 156133190ebfSVladimir Kondratiev 1562e00243faSLior David rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, hidden_ssid, is_go); 156333190ebfSVladimir Kondratiev if (rc) 156433190ebfSVladimir Kondratiev goto err_pcp_start; 156533190ebfSVladimir Kondratiev 1566e00243faSLior David rc = wil_bcast_init(vif); 156733190ebfSVladimir Kondratiev if (rc) 156833190ebfSVladimir Kondratiev goto err_bcast; 156933190ebfSVladimir Kondratiev 157033190ebfSVladimir Kondratiev goto out; /* success */ 157133190ebfSVladimir Kondratiev 157233190ebfSVladimir Kondratiev err_bcast: 1573e00243faSLior David wmi_pcp_stop(vif); 157433190ebfSVladimir Kondratiev err_pcp_start: 157533190ebfSVladimir Kondratiev netif_carrier_off(ndev); 15763ada9314SLior David if (!wil_has_other_active_ifaces(wil, ndev, false, true)) 15779953a782SLior David wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); 157833190ebfSVladimir Kondratiev out: 157933190ebfSVladimir Kondratiev mutex_unlock(&wil->mutex); 158033190ebfSVladimir Kondratiev return rc; 158133190ebfSVladimir Kondratiev } 158233190ebfSVladimir Kondratiev 15831bd922fcSVladimir Kondratiev static int wil_cfg80211_change_beacon(struct wiphy *wiphy, 15841bd922fcSVladimir Kondratiev struct net_device *ndev, 15851bd922fcSVladimir Kondratiev struct cfg80211_beacon_data *bcon) 15861bd922fcSVladimir Kondratiev { 15871bd922fcSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1588e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 15891bd922fcSVladimir Kondratiev int rc; 159033190ebfSVladimir Kondratiev u32 privacy = 0; 15911bd922fcSVladimir Kondratiev 1592e00243faSLior David wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid); 15931e7e5a0dSVladimir Kondratiev wil_print_bcon_data(bcon); 15941e7e5a0dSVladimir Kondratiev 1595c5a157e4SLior David if (bcon->tail && 1596c5a157e4SLior David cfg80211_find_ie(WLAN_EID_RSN, bcon->tail, 1597c5a157e4SLior David bcon->tail_len)) 159833190ebfSVladimir Kondratiev privacy = 1; 15991bd922fcSVladimir Kondratiev 160033190ebfSVladimir Kondratiev /* in case privacy has changed, need to restart the AP */ 1601e00243faSLior David if (vif->privacy != privacy) { 160233190ebfSVladimir Kondratiev struct wireless_dev *wdev = ndev->ieee80211_ptr; 160333190ebfSVladimir Kondratiev 160433190ebfSVladimir Kondratiev wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n", 1605e00243faSLior David vif->privacy, privacy); 160633190ebfSVladimir Kondratiev 160733190ebfSVladimir Kondratiev rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid, 160833190ebfSVladimir Kondratiev wdev->ssid_len, privacy, 160933190ebfSVladimir Kondratiev wdev->beacon_interval, 1610e00243faSLior David vif->channel, bcon, 1611e00243faSLior David vif->hidden_ssid, 1612e00243faSLior David vif->pbss); 161333190ebfSVladimir Kondratiev } else { 1614e00243faSLior David rc = _wil_cfg80211_set_ies(vif, bcon); 16151bd922fcSVladimir Kondratiev } 16161bd922fcSVladimir Kondratiev 161733190ebfSVladimir Kondratiev return rc; 16181bd922fcSVladimir Kondratiev } 16191bd922fcSVladimir Kondratiev 16202be7d22fSVladimir Kondratiev static int wil_cfg80211_start_ap(struct wiphy *wiphy, 16212be7d22fSVladimir Kondratiev struct net_device *ndev, 16222be7d22fSVladimir Kondratiev struct cfg80211_ap_settings *info) 16232be7d22fSVladimir Kondratiev { 162433190ebfSVladimir Kondratiev int rc; 16252be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 16262be7d22fSVladimir Kondratiev struct ieee80211_channel *channel = info->chandef.chan; 16272be7d22fSVladimir Kondratiev struct cfg80211_beacon_data *bcon = &info->beacon; 1628ca959773SVladimir Kondratiev struct cfg80211_crypto_settings *crypto = &info->crypto; 16298e52fe30SHamad Kadmany u8 hidden_ssid; 16302be7d22fSVladimir Kondratiev 1631af3db60aSLazar Alexei wil_dbg_misc(wil, "start_ap\n"); 1632ca959773SVladimir Kondratiev 16332be7d22fSVladimir Kondratiev if (!channel) { 16342be7d22fSVladimir Kondratiev wil_err(wil, "AP: No channel???\n"); 16352be7d22fSVladimir Kondratiev return -EINVAL; 16362be7d22fSVladimir Kondratiev } 16372be7d22fSVladimir Kondratiev 163833190ebfSVladimir Kondratiev switch (info->hidden_ssid) { 163933190ebfSVladimir Kondratiev case NL80211_HIDDEN_SSID_NOT_IN_USE: 164033190ebfSVladimir Kondratiev hidden_ssid = WMI_HIDDEN_SSID_DISABLED; 164133190ebfSVladimir Kondratiev break; 164233190ebfSVladimir Kondratiev 164333190ebfSVladimir Kondratiev case NL80211_HIDDEN_SSID_ZERO_LEN: 164433190ebfSVladimir Kondratiev hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY; 164533190ebfSVladimir Kondratiev break; 164633190ebfSVladimir Kondratiev 164733190ebfSVladimir Kondratiev case NL80211_HIDDEN_SSID_ZERO_CONTENTS: 164833190ebfSVladimir Kondratiev hidden_ssid = WMI_HIDDEN_SSID_CLEAR; 164933190ebfSVladimir Kondratiev break; 165033190ebfSVladimir Kondratiev 165133190ebfSVladimir Kondratiev default: 165233190ebfSVladimir Kondratiev wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid); 165333190ebfSVladimir Kondratiev return -EOPNOTSUPP; 165433190ebfSVladimir Kondratiev } 16557743882dSVladimir Kondratiev wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, 16562be7d22fSVladimir Kondratiev channel->center_freq, info->privacy ? "secure" : "open"); 1657ca959773SVladimir Kondratiev wil_dbg_misc(wil, "Privacy: %d auth_type %d\n", 1658ca959773SVladimir Kondratiev info->privacy, info->auth_type); 16598e52fe30SHamad Kadmany wil_dbg_misc(wil, "Hidden SSID mode: %d\n", 16608e52fe30SHamad Kadmany info->hidden_ssid); 1661ca959773SVladimir Kondratiev wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval, 1662ca959773SVladimir Kondratiev info->dtim_period); 1663eabb03b4SLior David wil_dbg_misc(wil, "PBSS %d\n", info->pbss); 16645eb443e9SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, 16655eb443e9SDedy Lansky info->ssid, info->ssid_len, true); 1666ca959773SVladimir Kondratiev wil_print_bcon_data(bcon); 1667ca959773SVladimir Kondratiev wil_print_crypto(wil, crypto); 16682be7d22fSVladimir Kondratiev 166933190ebfSVladimir Kondratiev rc = _wil_cfg80211_start_ap(wiphy, ndev, 167033190ebfSVladimir Kondratiev info->ssid, info->ssid_len, info->privacy, 167133190ebfSVladimir Kondratiev info->beacon_interval, channel->hw_value, 1672eabb03b4SLior David bcon, hidden_ssid, info->pbss); 1673c33407a8SVladimir Kondratiev 16742be7d22fSVladimir Kondratiev return rc; 16752be7d22fSVladimir Kondratiev } 16762be7d22fSVladimir Kondratiev 16772be7d22fSVladimir Kondratiev static int wil_cfg80211_stop_ap(struct wiphy *wiphy, 16782be7d22fSVladimir Kondratiev struct net_device *ndev) 16792be7d22fSVladimir Kondratiev { 16802be7d22fSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1681e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(ndev); 16823ada9314SLior David bool last; 16832be7d22fSVladimir Kondratiev 16843ada9314SLior David wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid); 1685ca959773SVladimir Kondratiev 1686c5e96c91SDedy Lansky netif_carrier_off(ndev); 16873ada9314SLior David last = !wil_has_other_active_ifaces(wil, ndev, false, true); 16883ada9314SLior David if (last) { 16899953a782SLior David wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); 1690c33407a8SVladimir Kondratiev wil_set_recovery_state(wil, fw_recovery_idle); 1691646d402dSHamad Kadmany set_bit(wil_status_resetting, wil->status); 16923ada9314SLior David } 1693646d402dSHamad Kadmany 16949c3bde56SVladimir Kondratiev mutex_lock(&wil->mutex); 16959c3bde56SVladimir Kondratiev 1696e00243faSLior David wmi_pcp_stop(vif); 16972be7d22fSVladimir Kondratiev 16983ada9314SLior David if (last) 169973d839aeSVladimir Kondratiev __wil_down(wil); 17003ada9314SLior David else 17013ada9314SLior David wil_bcast_fini(vif); 170273d839aeSVladimir Kondratiev 17039c3bde56SVladimir Kondratiev mutex_unlock(&wil->mutex); 170473d839aeSVladimir Kondratiev 170532a20d46SDedy Lansky return 0; 17062be7d22fSVladimir Kondratiev } 17072be7d22fSVladimir Kondratiev 1708849a564bSDedy Lansky static int wil_cfg80211_add_station(struct wiphy *wiphy, 1709849a564bSDedy Lansky struct net_device *dev, 1710849a564bSDedy Lansky const u8 *mac, 1711849a564bSDedy Lansky struct station_parameters *params) 1712849a564bSDedy Lansky { 1713e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 1714849a564bSDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1715849a564bSDedy Lansky 1716e00243faSLior David wil_dbg_misc(wil, "add station %pM aid %d mid %d\n", 1717e00243faSLior David mac, params->aid, vif->mid); 1718849a564bSDedy Lansky 1719849a564bSDedy Lansky if (!disable_ap_sme) { 1720849a564bSDedy Lansky wil_err(wil, "not supported with AP SME enabled\n"); 1721849a564bSDedy Lansky return -EOPNOTSUPP; 1722849a564bSDedy Lansky } 1723849a564bSDedy Lansky 1724849a564bSDedy Lansky if (params->aid > WIL_MAX_DMG_AID) { 1725849a564bSDedy Lansky wil_err(wil, "invalid aid\n"); 1726849a564bSDedy Lansky return -EINVAL; 1727849a564bSDedy Lansky } 1728849a564bSDedy Lansky 1729e00243faSLior David return wmi_new_sta(vif, mac, params->aid); 1730849a564bSDedy Lansky } 1731849a564bSDedy Lansky 17324d55a0a1SVladimir Kondratiev static int wil_cfg80211_del_station(struct wiphy *wiphy, 173389c771e5SJouni Malinen struct net_device *dev, 173489c771e5SJouni Malinen struct station_del_parameters *params) 17354d55a0a1SVladimir Kondratiev { 1736e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 17374d55a0a1SVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1738097638a0SVladimir Kondratiev 1739e00243faSLior David wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n", 1740e00243faSLior David params->mac, params->reason_code, vif->mid); 1741de9084efSVladimir Kondratiev 1742097638a0SVladimir Kondratiev mutex_lock(&wil->mutex); 1743e00243faSLior David wil6210_disconnect(vif, params->mac, params->reason_code, false); 1744097638a0SVladimir Kondratiev mutex_unlock(&wil->mutex); 1745097638a0SVladimir Kondratiev 17464d55a0a1SVladimir Kondratiev return 0; 17474d55a0a1SVladimir Kondratiev } 17484d55a0a1SVladimir Kondratiev 1749849a564bSDedy Lansky static int wil_cfg80211_change_station(struct wiphy *wiphy, 1750849a564bSDedy Lansky struct net_device *dev, 1751849a564bSDedy Lansky const u8 *mac, 1752849a564bSDedy Lansky struct station_parameters *params) 1753849a564bSDedy Lansky { 1754e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 1755849a564bSDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1756849a564bSDedy Lansky int authorize; 1757849a564bSDedy Lansky int cid, i; 1758849a564bSDedy Lansky struct vring_tx_data *txdata = NULL; 1759849a564bSDedy Lansky 1760e00243faSLior David wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n", 1761e00243faSLior David mac, params->sta_flags_mask, params->sta_flags_set, 1762e00243faSLior David vif->mid); 1763849a564bSDedy Lansky 1764849a564bSDedy Lansky if (!disable_ap_sme) { 1765849a564bSDedy Lansky wil_dbg_misc(wil, "not supported with AP SME enabled\n"); 1766849a564bSDedy Lansky return -EOPNOTSUPP; 1767849a564bSDedy Lansky } 1768849a564bSDedy Lansky 1769849a564bSDedy Lansky if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) 1770849a564bSDedy Lansky return 0; 1771849a564bSDedy Lansky 1772e00243faSLior David cid = wil_find_cid(wil, vif->mid, mac); 1773849a564bSDedy Lansky if (cid < 0) { 1774849a564bSDedy Lansky wil_err(wil, "station not found\n"); 1775849a564bSDedy Lansky return -ENOLINK; 1776849a564bSDedy Lansky } 1777849a564bSDedy Lansky 1778849a564bSDedy Lansky for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) 1779849a564bSDedy Lansky if (wil->vring2cid_tid[i][0] == cid) { 1780849a564bSDedy Lansky txdata = &wil->vring_tx_data[i]; 1781849a564bSDedy Lansky break; 1782849a564bSDedy Lansky } 1783849a564bSDedy Lansky 1784849a564bSDedy Lansky if (!txdata) { 1785849a564bSDedy Lansky wil_err(wil, "vring data not found\n"); 1786849a564bSDedy Lansky return -ENOLINK; 1787849a564bSDedy Lansky } 1788849a564bSDedy Lansky 1789849a564bSDedy Lansky authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED); 1790849a564bSDedy Lansky txdata->dot1x_open = authorize ? 1 : 0; 1791849a564bSDedy Lansky wil_dbg_misc(wil, "cid %d vring %d authorize %d\n", cid, i, 1792849a564bSDedy Lansky txdata->dot1x_open); 1793849a564bSDedy Lansky 1794849a564bSDedy Lansky return 0; 1795849a564bSDedy Lansky } 1796849a564bSDedy Lansky 179740822a90SVladimir Kondratiev /* probe_client handling */ 179840822a90SVladimir Kondratiev static void wil_probe_client_handle(struct wil6210_priv *wil, 1799e00243faSLior David struct wil6210_vif *vif, 180040822a90SVladimir Kondratiev struct wil_probe_client_req *req) 180140822a90SVladimir Kondratiev { 1802e00243faSLior David struct net_device *ndev = vif_to_ndev(vif); 180340822a90SVladimir Kondratiev struct wil_sta_info *sta = &wil->sta[req->cid]; 180440822a90SVladimir Kondratiev /* assume STA is alive if it is still connected, 180540822a90SVladimir Kondratiev * else FW will disconnect it 180640822a90SVladimir Kondratiev */ 180740822a90SVladimir Kondratiev bool alive = (sta->status == wil_sta_connected); 180840822a90SVladimir Kondratiev 1809c4b50cd3SVenkateswara Naralasetty cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, 1810c4b50cd3SVenkateswara Naralasetty 0, false, GFP_KERNEL); 181140822a90SVladimir Kondratiev } 181240822a90SVladimir Kondratiev 1813e00243faSLior David static struct list_head *next_probe_client(struct wil6210_vif *vif) 181440822a90SVladimir Kondratiev { 181540822a90SVladimir Kondratiev struct list_head *ret = NULL; 181640822a90SVladimir Kondratiev 1817e00243faSLior David mutex_lock(&vif->probe_client_mutex); 181840822a90SVladimir Kondratiev 1819e00243faSLior David if (!list_empty(&vif->probe_client_pending)) { 1820e00243faSLior David ret = vif->probe_client_pending.next; 182140822a90SVladimir Kondratiev list_del(ret); 182240822a90SVladimir Kondratiev } 182340822a90SVladimir Kondratiev 1824e00243faSLior David mutex_unlock(&vif->probe_client_mutex); 182540822a90SVladimir Kondratiev 182640822a90SVladimir Kondratiev return ret; 182740822a90SVladimir Kondratiev } 182840822a90SVladimir Kondratiev 182940822a90SVladimir Kondratiev void wil_probe_client_worker(struct work_struct *work) 183040822a90SVladimir Kondratiev { 1831e00243faSLior David struct wil6210_vif *vif = container_of(work, struct wil6210_vif, 183240822a90SVladimir Kondratiev probe_client_worker); 1833e00243faSLior David struct wil6210_priv *wil = vif_to_wil(vif); 183440822a90SVladimir Kondratiev struct wil_probe_client_req *req; 183540822a90SVladimir Kondratiev struct list_head *lh; 183640822a90SVladimir Kondratiev 1837e00243faSLior David while ((lh = next_probe_client(vif)) != NULL) { 183840822a90SVladimir Kondratiev req = list_entry(lh, struct wil_probe_client_req, list); 183940822a90SVladimir Kondratiev 1840e00243faSLior David wil_probe_client_handle(wil, vif, req); 184140822a90SVladimir Kondratiev kfree(req); 184240822a90SVladimir Kondratiev } 184340822a90SVladimir Kondratiev } 184440822a90SVladimir Kondratiev 1845e00243faSLior David void wil_probe_client_flush(struct wil6210_vif *vif) 184640822a90SVladimir Kondratiev { 184740822a90SVladimir Kondratiev struct wil_probe_client_req *req, *t; 1848e00243faSLior David struct wil6210_priv *wil = vif_to_wil(vif); 184940822a90SVladimir Kondratiev 1850af3db60aSLazar Alexei wil_dbg_misc(wil, "probe_client_flush\n"); 185140822a90SVladimir Kondratiev 1852e00243faSLior David mutex_lock(&vif->probe_client_mutex); 185340822a90SVladimir Kondratiev 1854e00243faSLior David list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) { 185540822a90SVladimir Kondratiev list_del(&req->list); 185640822a90SVladimir Kondratiev kfree(req); 185740822a90SVladimir Kondratiev } 185840822a90SVladimir Kondratiev 1859e00243faSLior David mutex_unlock(&vif->probe_client_mutex); 186040822a90SVladimir Kondratiev } 186140822a90SVladimir Kondratiev 186240822a90SVladimir Kondratiev static int wil_cfg80211_probe_client(struct wiphy *wiphy, 186340822a90SVladimir Kondratiev struct net_device *dev, 186440822a90SVladimir Kondratiev const u8 *peer, u64 *cookie) 186540822a90SVladimir Kondratiev { 186640822a90SVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1867e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 186840822a90SVladimir Kondratiev struct wil_probe_client_req *req; 1869e00243faSLior David int cid = wil_find_cid(wil, vif->mid, peer); 187040822a90SVladimir Kondratiev 1871e00243faSLior David wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n", 1872e00243faSLior David peer, cid, vif->mid); 187340822a90SVladimir Kondratiev 187440822a90SVladimir Kondratiev if (cid < 0) 187540822a90SVladimir Kondratiev return -ENOLINK; 187640822a90SVladimir Kondratiev 187740822a90SVladimir Kondratiev req = kzalloc(sizeof(*req), GFP_KERNEL); 187840822a90SVladimir Kondratiev if (!req) 187940822a90SVladimir Kondratiev return -ENOMEM; 188040822a90SVladimir Kondratiev 188140822a90SVladimir Kondratiev req->cid = cid; 188240822a90SVladimir Kondratiev req->cookie = cid; 188340822a90SVladimir Kondratiev 1884e00243faSLior David mutex_lock(&vif->probe_client_mutex); 1885e00243faSLior David list_add_tail(&req->list, &vif->probe_client_pending); 1886e00243faSLior David mutex_unlock(&vif->probe_client_mutex); 188740822a90SVladimir Kondratiev 188840822a90SVladimir Kondratiev *cookie = req->cookie; 1889e00243faSLior David queue_work(wil->wq_service, &vif->probe_client_worker); 189040822a90SVladimir Kondratiev return 0; 189140822a90SVladimir Kondratiev } 189240822a90SVladimir Kondratiev 189302beaf1aSVladimir Kondratiev static int wil_cfg80211_change_bss(struct wiphy *wiphy, 189402beaf1aSVladimir Kondratiev struct net_device *dev, 189502beaf1aSVladimir Kondratiev struct bss_parameters *params) 189602beaf1aSVladimir Kondratiev { 189702beaf1aSVladimir Kondratiev struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1898e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 189902beaf1aSVladimir Kondratiev 190002beaf1aSVladimir Kondratiev if (params->ap_isolate >= 0) { 1901e00243faSLior David wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n", 1902e00243faSLior David vif->mid, vif->ap_isolate, params->ap_isolate); 1903e00243faSLior David vif->ap_isolate = params->ap_isolate; 190402beaf1aSVladimir Kondratiev } 190502beaf1aSVladimir Kondratiev 190602beaf1aSVladimir Kondratiev return 0; 190702beaf1aSVladimir Kondratiev } 190802beaf1aSVladimir Kondratiev 19092c207eb8SMaya Erez static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, 19102c207eb8SMaya Erez struct net_device *dev, 19112c207eb8SMaya Erez bool enabled, int timeout) 19122c207eb8SMaya Erez { 19132c207eb8SMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy); 19142c207eb8SMaya Erez enum wmi_ps_profile_type ps_profile; 19152c207eb8SMaya Erez 19162c207eb8SMaya Erez wil_dbg_misc(wil, "enabled=%d, timeout=%d\n", 19172c207eb8SMaya Erez enabled, timeout); 19182c207eb8SMaya Erez 19192c207eb8SMaya Erez if (enabled) 19202c207eb8SMaya Erez ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT; 19212c207eb8SMaya Erez else 19222c207eb8SMaya Erez ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED; 19232c207eb8SMaya Erez 19248b068c03SLazar Alexei return wil_ps_update(wil, ps_profile); 19252c207eb8SMaya Erez } 19262c207eb8SMaya Erez 1927fe9ee51eSMaya Erez static int wil_cfg80211_suspend(struct wiphy *wiphy, 1928fe9ee51eSMaya Erez struct cfg80211_wowlan *wow) 1929fe9ee51eSMaya Erez { 1930fe9ee51eSMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1931fe9ee51eSMaya Erez int rc; 1932fe9ee51eSMaya Erez 1933fe9ee51eSMaya Erez /* Setting the wakeup trigger based on wow is TBD */ 1934fe9ee51eSMaya Erez 1935fe9ee51eSMaya Erez if (test_bit(wil_status_suspended, wil->status)) { 1936fe9ee51eSMaya Erez wil_dbg_pm(wil, "trying to suspend while suspended\n"); 1937fe9ee51eSMaya Erez return 0; 1938fe9ee51eSMaya Erez } 1939fe9ee51eSMaya Erez 1940fe9ee51eSMaya Erez rc = wil_can_suspend(wil, false); 1941fe9ee51eSMaya Erez if (rc) 1942fe9ee51eSMaya Erez goto out; 1943fe9ee51eSMaya Erez 1944fe9ee51eSMaya Erez wil_dbg_pm(wil, "suspending\n"); 1945fe9ee51eSMaya Erez 1946144a12a6SHamad Kadmany mutex_lock(&wil->mutex); 1947404bbb3cSLior David mutex_lock(&wil->vif_mutex); 1948144a12a6SHamad Kadmany wil_p2p_stop_radio_operations(wil); 19495bd60982SLior David wil_abort_scan_all_vifs(wil, true); 1950404bbb3cSLior David mutex_unlock(&wil->vif_mutex); 1951144a12a6SHamad Kadmany mutex_unlock(&wil->mutex); 1952fe9ee51eSMaya Erez 1953fe9ee51eSMaya Erez out: 1954fe9ee51eSMaya Erez return rc; 1955fe9ee51eSMaya Erez } 1956fe9ee51eSMaya Erez 1957fe9ee51eSMaya Erez static int wil_cfg80211_resume(struct wiphy *wiphy) 1958fe9ee51eSMaya Erez { 1959fe9ee51eSMaya Erez struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1960fe9ee51eSMaya Erez 1961fe9ee51eSMaya Erez wil_dbg_pm(wil, "resuming\n"); 1962fe9ee51eSMaya Erez 1963fe9ee51eSMaya Erez return 0; 1964fe9ee51eSMaya Erez } 1965fe9ee51eSMaya Erez 1966a5dc6883SDedy Lansky static int 1967a5dc6883SDedy Lansky wil_cfg80211_sched_scan_start(struct wiphy *wiphy, 1968a5dc6883SDedy Lansky struct net_device *dev, 1969a5dc6883SDedy Lansky struct cfg80211_sched_scan_request *request) 1970a5dc6883SDedy Lansky { 1971a5dc6883SDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy); 1972e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 1973a5dc6883SDedy Lansky int i, rc; 1974a5dc6883SDedy Lansky 1975e00243faSLior David if (vif->mid != 0) 1976e00243faSLior David return -EOPNOTSUPP; 1977e00243faSLior David 1978a5dc6883SDedy Lansky wil_dbg_misc(wil, 1979a5dc6883SDedy Lansky "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n", 1980a5dc6883SDedy Lansky request->n_ssids, request->ie_len, request->flags); 1981a5dc6883SDedy Lansky for (i = 0; i < request->n_ssids; i++) { 1982a5dc6883SDedy Lansky wil_dbg_misc(wil, "SSID[%d]:", i); 1983a5dc6883SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, 1984a5dc6883SDedy Lansky request->ssids[i].ssid, 1985a5dc6883SDedy Lansky request->ssids[i].ssid_len, true); 1986a5dc6883SDedy Lansky } 1987a5dc6883SDedy Lansky wil_dbg_misc(wil, "channels:"); 1988a5dc6883SDedy Lansky for (i = 0; i < request->n_channels; i++) 1989a5dc6883SDedy Lansky wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value, 1990a5dc6883SDedy Lansky i == request->n_channels - 1 ? "\n" : ""); 1991a5dc6883SDedy Lansky wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n", 1992a5dc6883SDedy Lansky request->n_match_sets, request->min_rssi_thold, 1993a5dc6883SDedy Lansky request->delay); 1994a5dc6883SDedy Lansky for (i = 0; i < request->n_match_sets; i++) { 1995a5dc6883SDedy Lansky struct cfg80211_match_set *ms = &request->match_sets[i]; 1996a5dc6883SDedy Lansky 1997a5dc6883SDedy Lansky wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n", 1998a5dc6883SDedy Lansky i, ms->rssi_thold); 1999a5dc6883SDedy Lansky wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, 2000a5dc6883SDedy Lansky ms->ssid.ssid, 2001a5dc6883SDedy Lansky ms->ssid.ssid_len, true); 2002a5dc6883SDedy Lansky } 2003a5dc6883SDedy Lansky wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans); 2004a5dc6883SDedy Lansky for (i = 0; i < request->n_scan_plans; i++) { 2005a5dc6883SDedy Lansky struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i]; 2006a5dc6883SDedy Lansky 2007a5dc6883SDedy Lansky wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n", 2008a5dc6883SDedy Lansky i, sp->interval, sp->iterations); 2009a5dc6883SDedy Lansky } 2010a5dc6883SDedy Lansky 2011e00243faSLior David rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, 2012e00243faSLior David request->ie_len, request->ie); 2013a5dc6883SDedy Lansky if (rc) 2014a5dc6883SDedy Lansky return rc; 2015a5dc6883SDedy Lansky return wmi_start_sched_scan(wil, request); 2016a5dc6883SDedy Lansky } 2017a5dc6883SDedy Lansky 2018a5dc6883SDedy Lansky static int 2019a5dc6883SDedy Lansky wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, 2020a5dc6883SDedy Lansky u64 reqid) 2021a5dc6883SDedy Lansky { 2022a5dc6883SDedy Lansky struct wil6210_priv *wil = wiphy_to_wil(wiphy); 2023e00243faSLior David struct wil6210_vif *vif = ndev_to_vif(dev); 2024a5dc6883SDedy Lansky int rc; 2025a5dc6883SDedy Lansky 2026e00243faSLior David if (vif->mid != 0) 2027e00243faSLior David return -EOPNOTSUPP; 2028e00243faSLior David 2029a5dc6883SDedy Lansky rc = wmi_stop_sched_scan(wil); 2030a5dc6883SDedy Lansky /* device would return error if it thinks PNO is already stopped. 2031a5dc6883SDedy Lansky * ignore the return code so user space and driver gets back in-sync 2032a5dc6883SDedy Lansky */ 2033a5dc6883SDedy Lansky wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc); 2034a5dc6883SDedy Lansky 2035a5dc6883SDedy Lansky return 0; 2036a5dc6883SDedy Lansky } 2037a5dc6883SDedy Lansky 2038b59eb961SBhumika Goyal static const struct cfg80211_ops wil_cfg80211_ops = { 20394332cac1SLior David .add_virtual_intf = wil_cfg80211_add_iface, 20404332cac1SLior David .del_virtual_intf = wil_cfg80211_del_iface, 20412be7d22fSVladimir Kondratiev .scan = wil_cfg80211_scan, 2042035859a5SMaya Erez .abort_scan = wil_cfg80211_abort_scan, 20432be7d22fSVladimir Kondratiev .connect = wil_cfg80211_connect, 20442be7d22fSVladimir Kondratiev .disconnect = wil_cfg80211_disconnect, 20453fea18d0SLior David .set_wiphy_params = wil_cfg80211_set_wiphy_params, 20462be7d22fSVladimir Kondratiev .change_virtual_intf = wil_cfg80211_change_iface, 20472be7d22fSVladimir Kondratiev .get_station = wil_cfg80211_get_station, 2048ef28afdbSVladimir Kondratiev .dump_station = wil_cfg80211_dump_station, 20491647f12fSVladimir Kondratiev .remain_on_channel = wil_remain_on_channel, 20501647f12fSVladimir Kondratiev .cancel_remain_on_channel = wil_cancel_remain_on_channel, 20511647f12fSVladimir Kondratiev .mgmt_tx = wil_cfg80211_mgmt_tx, 20522be7d22fSVladimir Kondratiev .set_monitor_channel = wil_cfg80211_set_channel, 20532be7d22fSVladimir Kondratiev .add_key = wil_cfg80211_add_key, 20542be7d22fSVladimir Kondratiev .del_key = wil_cfg80211_del_key, 20552be7d22fSVladimir Kondratiev .set_default_key = wil_cfg80211_set_default_key, 20562be7d22fSVladimir Kondratiev /* AP mode */ 20571bd922fcSVladimir Kondratiev .change_beacon = wil_cfg80211_change_beacon, 20582be7d22fSVladimir Kondratiev .start_ap = wil_cfg80211_start_ap, 20592be7d22fSVladimir Kondratiev .stop_ap = wil_cfg80211_stop_ap, 2060849a564bSDedy Lansky .add_station = wil_cfg80211_add_station, 20614d55a0a1SVladimir Kondratiev .del_station = wil_cfg80211_del_station, 2062849a564bSDedy Lansky .change_station = wil_cfg80211_change_station, 206340822a90SVladimir Kondratiev .probe_client = wil_cfg80211_probe_client, 206402beaf1aSVladimir Kondratiev .change_bss = wil_cfg80211_change_bss, 20654332cac1SLior David /* P2P device */ 20664332cac1SLior David .start_p2p_device = wil_cfg80211_start_p2p_device, 20674332cac1SLior David .stop_p2p_device = wil_cfg80211_stop_p2p_device, 20682c207eb8SMaya Erez .set_power_mgmt = wil_cfg80211_set_power_mgmt, 2069fe9ee51eSMaya Erez .suspend = wil_cfg80211_suspend, 2070fe9ee51eSMaya Erez .resume = wil_cfg80211_resume, 2071a5dc6883SDedy Lansky .sched_scan_start = wil_cfg80211_sched_scan_start, 2072a5dc6883SDedy Lansky .sched_scan_stop = wil_cfg80211_sched_scan_stop, 20732be7d22fSVladimir Kondratiev }; 20742be7d22fSVladimir Kondratiev 20752be7d22fSVladimir Kondratiev static void wil_wiphy_init(struct wiphy *wiphy) 20762be7d22fSVladimir Kondratiev { 20778e52fe30SHamad Kadmany wiphy->max_scan_ssids = 1; 207877c91295SVladimir Kondratiev wiphy->max_scan_ie_len = WMI_MAX_IE_LEN; 2079e6d68341SDedy Lansky wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS; 20802be7d22fSVladimir Kondratiev wiphy->max_num_pmkids = 0 /* TODO: */; 20812be7d22fSVladimir Kondratiev wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 20822be7d22fSVladimir Kondratiev BIT(NL80211_IFTYPE_AP) | 2083e6d68341SDedy Lansky BIT(NL80211_IFTYPE_P2P_CLIENT) | 2084e6d68341SDedy Lansky BIT(NL80211_IFTYPE_P2P_GO) | 20854332cac1SLior David BIT(NL80211_IFTYPE_P2P_DEVICE) | 2086e6d68341SDedy Lansky BIT(NL80211_IFTYPE_MONITOR); 2087849a564bSDedy Lansky wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | 20882c207eb8SMaya Erez WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 20892c207eb8SMaya Erez WIPHY_FLAG_PS_ON_BY_DEFAULT; 2090849a564bSDedy Lansky if (!disable_ap_sme) 2091849a564bSDedy Lansky wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; 20929cf10d62SVladimir Kondratiev dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", 20932be7d22fSVladimir Kondratiev __func__, wiphy->flags); 20942be7d22fSVladimir Kondratiev wiphy->probe_resp_offload = 20952be7d22fSVladimir Kondratiev NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 20962be7d22fSVladimir Kondratiev NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | 20972be7d22fSVladimir Kondratiev NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; 20982be7d22fSVladimir Kondratiev 209957fbcce3SJohannes Berg wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz; 21002be7d22fSVladimir Kondratiev 210130868f5dSDedy Lansky /* may change after reading FW capabilities */ 2102b8b33a3aSVladimir Kondratiev wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; 21032be7d22fSVladimir Kondratiev 21042be7d22fSVladimir Kondratiev wiphy->cipher_suites = wil_cipher_suites; 21052be7d22fSVladimir Kondratiev wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); 21062be7d22fSVladimir Kondratiev wiphy->mgmt_stypes = wil_mgmt_stypes; 2107713c8a29SVladimir Kondratiev wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; 21080216a895SLior David 21090216a895SLior David wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands); 21100216a895SLior David wiphy->vendor_commands = wil_nl80211_vendor_commands; 2111d1fbf075SMaya Erez 2112d1fbf075SMaya Erez #ifdef CONFIG_PM 2113d1fbf075SMaya Erez wiphy->wowlan = &wil_wowlan_support; 2114d1fbf075SMaya Erez #endif 21152be7d22fSVladimir Kondratiev } 21162be7d22fSVladimir Kondratiev 21177bfe9e22SLior David int wil_cfg80211_iface_combinations_from_fw( 21187bfe9e22SLior David struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc) 21197bfe9e22SLior David { 21207bfe9e22SLior David struct wiphy *wiphy = wil_to_wiphy(wil); 21217bfe9e22SLior David u32 total_limits = 0; 21227bfe9e22SLior David u16 n_combos; 21237bfe9e22SLior David const struct wil_fw_concurrency_combo *combo; 21247bfe9e22SLior David const struct wil_fw_concurrency_limit *limit; 21257bfe9e22SLior David struct ieee80211_iface_combination *iface_combinations; 21267bfe9e22SLior David struct ieee80211_iface_limit *iface_limit; 21277bfe9e22SLior David int i, j; 21287bfe9e22SLior David 21297bfe9e22SLior David if (wiphy->iface_combinations) { 21307bfe9e22SLior David wil_dbg_misc(wil, "iface_combinations already set, skipping\n"); 21317bfe9e22SLior David return 0; 21327bfe9e22SLior David } 21337bfe9e22SLior David 21347bfe9e22SLior David combo = conc->combos; 21357bfe9e22SLior David n_combos = le16_to_cpu(conc->n_combos); 21367bfe9e22SLior David for (i = 0; i < n_combos; i++) { 21377bfe9e22SLior David total_limits += combo->n_limits; 21387bfe9e22SLior David limit = combo->limits + combo->n_limits; 21397bfe9e22SLior David combo = (struct wil_fw_concurrency_combo *)limit; 21407bfe9e22SLior David } 21417bfe9e22SLior David 21427bfe9e22SLior David iface_combinations = 21437bfe9e22SLior David kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) + 21447bfe9e22SLior David total_limits * sizeof(struct ieee80211_iface_limit), 21457bfe9e22SLior David GFP_KERNEL); 21467bfe9e22SLior David if (!iface_combinations) 21477bfe9e22SLior David return -ENOMEM; 21487bfe9e22SLior David iface_limit = (struct ieee80211_iface_limit *)(iface_combinations + 21497bfe9e22SLior David n_combos); 21507bfe9e22SLior David combo = conc->combos; 21517bfe9e22SLior David for (i = 0; i < n_combos; i++) { 21527bfe9e22SLior David iface_combinations[i].max_interfaces = combo->max_interfaces; 21537bfe9e22SLior David iface_combinations[i].num_different_channels = 21547bfe9e22SLior David combo->n_diff_channels; 21557bfe9e22SLior David iface_combinations[i].beacon_int_infra_match = 21567bfe9e22SLior David combo->same_bi; 21577bfe9e22SLior David iface_combinations[i].n_limits = combo->n_limits; 21587bfe9e22SLior David wil_dbg_misc(wil, 21597bfe9e22SLior David "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n", 21607bfe9e22SLior David i, iface_combinations[i].max_interfaces, 21617bfe9e22SLior David iface_combinations[i].num_different_channels, 21627bfe9e22SLior David iface_combinations[i].beacon_int_infra_match); 21637bfe9e22SLior David limit = combo->limits; 21647bfe9e22SLior David for (j = 0; j < combo->n_limits; j++) { 21657bfe9e22SLior David iface_limit[j].max = le16_to_cpu(limit[j].max); 21667bfe9e22SLior David iface_limit[j].types = le16_to_cpu(limit[j].types); 21677bfe9e22SLior David wil_dbg_misc(wil, 21687bfe9e22SLior David "limit %d: max %d types 0x%x\n", j, 21697bfe9e22SLior David iface_limit[j].max, iface_limit[j].types); 21707bfe9e22SLior David } 21717bfe9e22SLior David iface_combinations[i].limits = iface_limit; 21727bfe9e22SLior David iface_limit += combo->n_limits; 21737bfe9e22SLior David limit += combo->n_limits; 21747bfe9e22SLior David combo = (struct wil_fw_concurrency_combo *)limit; 21757bfe9e22SLior David } 21767bfe9e22SLior David 21774aebd3bdSLior David wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids); 21784aebd3bdSLior David wil->max_vifs = conc->n_mids + 1; /* including main interface */ 21794aebd3bdSLior David if (wil->max_vifs > WIL_MAX_VIFS) { 21804aebd3bdSLior David wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n", 21814aebd3bdSLior David WIL_MAX_VIFS, wil->max_vifs); 21824aebd3bdSLior David wil->max_vifs = WIL_MAX_VIFS; 21834aebd3bdSLior David } 21847bfe9e22SLior David wiphy->n_iface_combinations = n_combos; 21857bfe9e22SLior David wiphy->iface_combinations = iface_combinations; 21867bfe9e22SLior David return 0; 21877bfe9e22SLior David } 21887bfe9e22SLior David 21899f38f286SLior David struct wil6210_priv *wil_cfg80211_init(struct device *dev) 21902be7d22fSVladimir Kondratiev { 21919f38f286SLior David struct wiphy *wiphy; 21929f38f286SLior David struct wil6210_priv *wil; 21939f38f286SLior David struct ieee80211_channel *ch; 21942be7d22fSVladimir Kondratiev 21959cf10d62SVladimir Kondratiev dev_dbg(dev, "%s()\n", __func__); 21969cf10d62SVladimir Kondratiev 21979f38f286SLior David /* Note: the wireless_dev structure is no longer allocated here. 21989f38f286SLior David * Instead, it is allocated as part of the net_device structure 21999f38f286SLior David * for main interface and each VIF. 22009f38f286SLior David */ 22019f38f286SLior David wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv)); 22029f38f286SLior David if (!wiphy) 22032be7d22fSVladimir Kondratiev return ERR_PTR(-ENOMEM); 22042be7d22fSVladimir Kondratiev 22059f38f286SLior David set_wiphy_dev(wiphy, dev); 22069f38f286SLior David wil_wiphy_init(wiphy); 22079f38f286SLior David 22089f38f286SLior David wil = wiphy_to_wil(wiphy); 22099f38f286SLior David wil->wiphy = wiphy; 22109f38f286SLior David 22119f38f286SLior David /* default monitor channel */ 22129f38f286SLior David ch = wiphy->bands[NL80211_BAND_60GHZ]->channels; 22139f38f286SLior David cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT); 22149f38f286SLior David 22159f38f286SLior David return wil; 22162be7d22fSVladimir Kondratiev } 22172be7d22fSVladimir Kondratiev 22189f38f286SLior David void wil_cfg80211_deinit(struct wil6210_priv *wil) 22192be7d22fSVladimir Kondratiev { 22209f38f286SLior David struct wiphy *wiphy = wil_to_wiphy(wil); 22212be7d22fSVladimir Kondratiev 22229cf10d62SVladimir Kondratiev dev_dbg(wil_to_dev(wil), "%s()\n", __func__); 22239cf10d62SVladimir Kondratiev 22249f38f286SLior David if (!wiphy) 22252be7d22fSVladimir Kondratiev return; 22262be7d22fSVladimir Kondratiev 22277bfe9e22SLior David kfree(wiphy->iface_combinations); 22287bfe9e22SLior David wiphy->iface_combinations = NULL; 22297bfe9e22SLior David 22309f38f286SLior David wiphy_free(wiphy); 22319f38f286SLior David /* do not access wil6210_priv after returning from here */ 22322be7d22fSVladimir Kondratiev } 22334332cac1SLior David 22344332cac1SLior David void wil_p2p_wdev_free(struct wil6210_priv *wil) 22354332cac1SLior David { 22364332cac1SLior David struct wireless_dev *p2p_wdev; 22374332cac1SLior David 2238404bbb3cSLior David mutex_lock(&wil->vif_mutex); 22394332cac1SLior David p2p_wdev = wil->p2p_wdev; 22404332cac1SLior David wil->p2p_wdev = NULL; 2241e00243faSLior David wil->radio_wdev = wil->main_ndev->ieee80211_ptr; 2242404bbb3cSLior David mutex_unlock(&wil->vif_mutex); 2243d35c2b6fSMaya Erez if (p2p_wdev) { 22444332cac1SLior David cfg80211_unregister_wdev(p2p_wdev); 22455bd60982SLior David kfree(p2p_wdev); 22464332cac1SLior David } 22474332cac1SLior David } 22480216a895SLior David 22490216a895SLior David static int wil_rf_sector_status_to_rc(u8 status) 22500216a895SLior David { 22510216a895SLior David switch (status) { 22520216a895SLior David case WMI_RF_SECTOR_STATUS_SUCCESS: 22530216a895SLior David return 0; 22540216a895SLior David case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR: 22550216a895SLior David return -EINVAL; 22560216a895SLior David case WMI_RF_SECTOR_STATUS_BUSY_ERROR: 22570216a895SLior David return -EAGAIN; 22580216a895SLior David case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR: 22590216a895SLior David return -EOPNOTSUPP; 22600216a895SLior David default: 22610216a895SLior David return -EINVAL; 22620216a895SLior David } 22630216a895SLior David } 22640216a895SLior David 22650216a895SLior David static int wil_rf_sector_get_cfg(struct wiphy *wiphy, 22660216a895SLior David struct wireless_dev *wdev, 22670216a895SLior David const void *data, int data_len) 22680216a895SLior David { 22690216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev); 2270e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 22710216a895SLior David int rc; 22720216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; 22730216a895SLior David u16 sector_index; 22740216a895SLior David u8 sector_type; 22750216a895SLior David u32 rf_modules_vec; 22760216a895SLior David struct wmi_get_rf_sector_params_cmd cmd; 22770216a895SLior David struct { 22780216a895SLior David struct wmi_cmd_hdr wmi; 22790216a895SLior David struct wmi_get_rf_sector_params_done_event evt; 22800216a895SLior David } __packed reply; 22810216a895SLior David struct sk_buff *msg; 22820216a895SLior David struct nlattr *nl_cfgs, *nl_cfg; 22830216a895SLior David u32 i; 22840216a895SLior David struct wmi_rf_sector_info *si; 22850216a895SLior David 22860216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) 22870216a895SLior David return -EOPNOTSUPP; 22880216a895SLior David 22890216a895SLior David rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, 22900216a895SLior David wil_rf_sector_policy, NULL); 22910216a895SLior David if (rc) { 22920216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n"); 22930216a895SLior David return rc; 22940216a895SLior David } 22950216a895SLior David 22960216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || 22970216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || 22980216a895SLior David !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) { 22990216a895SLior David wil_err(wil, "Invalid rf sector spec\n"); 23000216a895SLior David return -EINVAL; 23010216a895SLior David } 23020216a895SLior David 23030216a895SLior David sector_index = nla_get_u16( 23040216a895SLior David tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); 23050216a895SLior David if (sector_index >= WIL_MAX_RF_SECTORS) { 23060216a895SLior David wil_err(wil, "Invalid sector index %d\n", sector_index); 23070216a895SLior David return -EINVAL; 23080216a895SLior David } 23090216a895SLior David 23100216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); 23110216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { 23120216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type); 23130216a895SLior David return -EINVAL; 23140216a895SLior David } 23150216a895SLior David 23160216a895SLior David rf_modules_vec = nla_get_u32( 23170216a895SLior David tb[QCA_ATTR_DMG_RF_MODULE_MASK]); 23180216a895SLior David if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) { 23190216a895SLior David wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec); 23200216a895SLior David return -EINVAL; 23210216a895SLior David } 23220216a895SLior David 23230216a895SLior David cmd.sector_idx = cpu_to_le16(sector_index); 23240216a895SLior David cmd.sector_type = sector_type; 23250216a895SLior David cmd.rf_modules_vec = rf_modules_vec & 0xFF; 23260216a895SLior David memset(&reply, 0, sizeof(reply)); 2327e00243faSLior David rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid, 2328e00243faSLior David &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID, 23290216a895SLior David &reply, sizeof(reply), 23300216a895SLior David 500); 23310216a895SLior David if (rc) 23320216a895SLior David return rc; 23330216a895SLior David if (reply.evt.status) { 23340216a895SLior David wil_err(wil, "get rf sector cfg failed with status %d\n", 23350216a895SLior David reply.evt.status); 23360216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status); 23370216a895SLior David } 23380216a895SLior David 23390216a895SLior David msg = cfg80211_vendor_cmd_alloc_reply_skb( 23400216a895SLior David wiphy, 64 * WMI_MAX_RF_MODULES_NUM); 23410216a895SLior David if (!msg) 23420216a895SLior David return -ENOMEM; 23430216a895SLior David 23440216a895SLior David if (nla_put_u64_64bit(msg, QCA_ATTR_TSF, 23450216a895SLior David le64_to_cpu(reply.evt.tsf), 23460216a895SLior David QCA_ATTR_PAD)) 23470216a895SLior David goto nla_put_failure; 23480216a895SLior David 23490216a895SLior David nl_cfgs = nla_nest_start(msg, QCA_ATTR_DMG_RF_SECTOR_CFG); 23500216a895SLior David if (!nl_cfgs) 23510216a895SLior David goto nla_put_failure; 23520216a895SLior David for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) { 23530216a895SLior David if (!(rf_modules_vec & BIT(i))) 23540216a895SLior David continue; 23550216a895SLior David nl_cfg = nla_nest_start(msg, i); 23560216a895SLior David if (!nl_cfg) 23570216a895SLior David goto nla_put_failure; 23580216a895SLior David si = &reply.evt.sectors_info[i]; 23590216a895SLior David if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, 23600216a895SLior David i) || 23610216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, 23620216a895SLior David le32_to_cpu(si->etype0)) || 23630216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, 23640216a895SLior David le32_to_cpu(si->etype1)) || 23650216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, 23660216a895SLior David le32_to_cpu(si->etype2)) || 23670216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, 23680216a895SLior David le32_to_cpu(si->psh_hi)) || 23690216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, 23700216a895SLior David le32_to_cpu(si->psh_lo)) || 23710216a895SLior David nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, 23720216a895SLior David le32_to_cpu(si->dtype_swch_off))) 23730216a895SLior David goto nla_put_failure; 23740216a895SLior David nla_nest_end(msg, nl_cfg); 23750216a895SLior David } 23760216a895SLior David 23770216a895SLior David nla_nest_end(msg, nl_cfgs); 23780216a895SLior David rc = cfg80211_vendor_cmd_reply(msg); 23790216a895SLior David return rc; 23800216a895SLior David nla_put_failure: 23810216a895SLior David kfree_skb(msg); 23820216a895SLior David return -ENOBUFS; 23830216a895SLior David } 23840216a895SLior David 23850216a895SLior David static int wil_rf_sector_set_cfg(struct wiphy *wiphy, 23860216a895SLior David struct wireless_dev *wdev, 23870216a895SLior David const void *data, int data_len) 23880216a895SLior David { 23890216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev); 2390e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 23910216a895SLior David int rc, tmp; 23920216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; 23930216a895SLior David struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1]; 23940216a895SLior David u16 sector_index, rf_module_index; 23950216a895SLior David u8 sector_type; 23960216a895SLior David u32 rf_modules_vec = 0; 23970216a895SLior David struct wmi_set_rf_sector_params_cmd cmd; 23980216a895SLior David struct { 23990216a895SLior David struct wmi_cmd_hdr wmi; 24000216a895SLior David struct wmi_set_rf_sector_params_done_event evt; 24010216a895SLior David } __packed reply; 24020216a895SLior David struct nlattr *nl_cfg; 24030216a895SLior David struct wmi_rf_sector_info *si; 24040216a895SLior David 24050216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) 24060216a895SLior David return -EOPNOTSUPP; 24070216a895SLior David 24080216a895SLior David rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, 24090216a895SLior David wil_rf_sector_policy, NULL); 24100216a895SLior David if (rc) { 24110216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n"); 24120216a895SLior David return rc; 24130216a895SLior David } 24140216a895SLior David 24150216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || 24160216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || 24170216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) { 24180216a895SLior David wil_err(wil, "Invalid rf sector spec\n"); 24190216a895SLior David return -EINVAL; 24200216a895SLior David } 24210216a895SLior David 24220216a895SLior David sector_index = nla_get_u16( 24230216a895SLior David tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); 24240216a895SLior David if (sector_index >= WIL_MAX_RF_SECTORS) { 24250216a895SLior David wil_err(wil, "Invalid sector index %d\n", sector_index); 24260216a895SLior David return -EINVAL; 24270216a895SLior David } 24280216a895SLior David 24290216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); 24300216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { 24310216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type); 24320216a895SLior David return -EINVAL; 24330216a895SLior David } 24340216a895SLior David 24350216a895SLior David memset(&cmd, 0, sizeof(cmd)); 24360216a895SLior David 24370216a895SLior David cmd.sector_idx = cpu_to_le16(sector_index); 24380216a895SLior David cmd.sector_type = sector_type; 24390216a895SLior David nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG], 24400216a895SLior David tmp) { 24410216a895SLior David rc = nla_parse_nested(tb2, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX, 24420216a895SLior David nl_cfg, wil_rf_sector_cfg_policy, 24430216a895SLior David NULL); 24440216a895SLior David if (rc) { 24450216a895SLior David wil_err(wil, "invalid sector cfg\n"); 24460216a895SLior David return -EINVAL; 24470216a895SLior David } 24480216a895SLior David 24490216a895SLior David if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] || 24500216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] || 24510216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] || 24520216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] || 24530216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] || 24540216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] || 24550216a895SLior David !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) { 24560216a895SLior David wil_err(wil, "missing cfg params\n"); 24570216a895SLior David return -EINVAL; 24580216a895SLior David } 24590216a895SLior David 24600216a895SLior David rf_module_index = nla_get_u8( 24610216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]); 24620216a895SLior David if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) { 24630216a895SLior David wil_err(wil, "invalid RF module index %d\n", 24640216a895SLior David rf_module_index); 24650216a895SLior David return -EINVAL; 24660216a895SLior David } 24670216a895SLior David rf_modules_vec |= BIT(rf_module_index); 24680216a895SLior David si = &cmd.sectors_info[rf_module_index]; 24690216a895SLior David si->etype0 = cpu_to_le32(nla_get_u32( 24700216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0])); 24710216a895SLior David si->etype1 = cpu_to_le32(nla_get_u32( 24720216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1])); 24730216a895SLior David si->etype2 = cpu_to_le32(nla_get_u32( 24740216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2])); 24750216a895SLior David si->psh_hi = cpu_to_le32(nla_get_u32( 24760216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI])); 24770216a895SLior David si->psh_lo = cpu_to_le32(nla_get_u32( 24780216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO])); 24790216a895SLior David si->dtype_swch_off = cpu_to_le32(nla_get_u32( 24800216a895SLior David tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16])); 24810216a895SLior David } 24820216a895SLior David 24830216a895SLior David cmd.rf_modules_vec = rf_modules_vec & 0xFF; 24840216a895SLior David memset(&reply, 0, sizeof(reply)); 2485e00243faSLior David rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid, 2486e00243faSLior David &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID, 24870216a895SLior David &reply, sizeof(reply), 24880216a895SLior David 500); 24890216a895SLior David if (rc) 24900216a895SLior David return rc; 24910216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status); 24920216a895SLior David } 24930216a895SLior David 24940216a895SLior David static int wil_rf_sector_get_selected(struct wiphy *wiphy, 24950216a895SLior David struct wireless_dev *wdev, 24960216a895SLior David const void *data, int data_len) 24970216a895SLior David { 24980216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev); 2499e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 25000216a895SLior David int rc; 25010216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; 25020216a895SLior David u8 sector_type, mac_addr[ETH_ALEN]; 25030216a895SLior David int cid = 0; 25040216a895SLior David struct wmi_get_selected_rf_sector_index_cmd cmd; 25050216a895SLior David struct { 25060216a895SLior David struct wmi_cmd_hdr wmi; 25070216a895SLior David struct wmi_get_selected_rf_sector_index_done_event evt; 25080216a895SLior David } __packed reply; 25090216a895SLior David struct sk_buff *msg; 25100216a895SLior David 25110216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) 25120216a895SLior David return -EOPNOTSUPP; 25130216a895SLior David 25140216a895SLior David rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, 25150216a895SLior David wil_rf_sector_policy, NULL); 25160216a895SLior David if (rc) { 25170216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n"); 25180216a895SLior David return rc; 25190216a895SLior David } 25200216a895SLior David 25210216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { 25220216a895SLior David wil_err(wil, "Invalid rf sector spec\n"); 25230216a895SLior David return -EINVAL; 25240216a895SLior David } 25250216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); 25260216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { 25270216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type); 25280216a895SLior David return -EINVAL; 25290216a895SLior David } 25300216a895SLior David 25310216a895SLior David if (tb[QCA_ATTR_MAC_ADDR]) { 25320216a895SLior David ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); 2533e00243faSLior David cid = wil_find_cid(wil, vif->mid, mac_addr); 25340216a895SLior David if (cid < 0) { 25350216a895SLior David wil_err(wil, "invalid MAC address %pM\n", mac_addr); 25360216a895SLior David return -ENOENT; 25370216a895SLior David } 25380216a895SLior David } else { 25395bd60982SLior David if (test_bit(wil_vif_fwconnected, vif->status)) { 25400216a895SLior David wil_err(wil, "must specify MAC address when connected\n"); 25410216a895SLior David return -EINVAL; 25420216a895SLior David } 25430216a895SLior David } 25440216a895SLior David 25450216a895SLior David memset(&cmd, 0, sizeof(cmd)); 25460216a895SLior David cmd.cid = (u8)cid; 25470216a895SLior David cmd.sector_type = sector_type; 25480216a895SLior David memset(&reply, 0, sizeof(reply)); 2549e00243faSLior David rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid, 25500216a895SLior David &cmd, sizeof(cmd), 25510216a895SLior David WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, 25520216a895SLior David &reply, sizeof(reply), 25530216a895SLior David 500); 25540216a895SLior David if (rc) 25550216a895SLior David return rc; 25560216a895SLior David if (reply.evt.status) { 25570216a895SLior David wil_err(wil, "get rf selected sector cfg failed with status %d\n", 25580216a895SLior David reply.evt.status); 25590216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status); 25600216a895SLior David } 25610216a895SLior David 25620216a895SLior David msg = cfg80211_vendor_cmd_alloc_reply_skb( 25630216a895SLior David wiphy, 64 * WMI_MAX_RF_MODULES_NUM); 25640216a895SLior David if (!msg) 25650216a895SLior David return -ENOMEM; 25660216a895SLior David 25670216a895SLior David if (nla_put_u64_64bit(msg, QCA_ATTR_TSF, 25680216a895SLior David le64_to_cpu(reply.evt.tsf), 25690216a895SLior David QCA_ATTR_PAD) || 25700216a895SLior David nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX, 25710216a895SLior David le16_to_cpu(reply.evt.sector_idx))) 25720216a895SLior David goto nla_put_failure; 25730216a895SLior David 25740216a895SLior David rc = cfg80211_vendor_cmd_reply(msg); 25750216a895SLior David return rc; 25760216a895SLior David nla_put_failure: 25770216a895SLior David kfree_skb(msg); 25780216a895SLior David return -ENOBUFS; 25790216a895SLior David } 25800216a895SLior David 25810216a895SLior David static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil, 2582e00243faSLior David u8 mid, u16 sector_index, 25830216a895SLior David u8 sector_type, u8 cid) 25840216a895SLior David { 25850216a895SLior David struct wmi_set_selected_rf_sector_index_cmd cmd; 25860216a895SLior David struct { 25870216a895SLior David struct wmi_cmd_hdr wmi; 25880216a895SLior David struct wmi_set_selected_rf_sector_index_done_event evt; 25890216a895SLior David } __packed reply; 25900216a895SLior David int rc; 25910216a895SLior David 25920216a895SLior David memset(&cmd, 0, sizeof(cmd)); 25930216a895SLior David cmd.sector_idx = cpu_to_le16(sector_index); 25940216a895SLior David cmd.sector_type = sector_type; 25950216a895SLior David cmd.cid = (u8)cid; 25960216a895SLior David memset(&reply, 0, sizeof(reply)); 2597e00243faSLior David rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid, 25980216a895SLior David &cmd, sizeof(cmd), 25990216a895SLior David WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, 26000216a895SLior David &reply, sizeof(reply), 26010216a895SLior David 500); 26020216a895SLior David if (rc) 26030216a895SLior David return rc; 26040216a895SLior David return wil_rf_sector_status_to_rc(reply.evt.status); 26050216a895SLior David } 26060216a895SLior David 26070216a895SLior David static int wil_rf_sector_set_selected(struct wiphy *wiphy, 26080216a895SLior David struct wireless_dev *wdev, 26090216a895SLior David const void *data, int data_len) 26100216a895SLior David { 26110216a895SLior David struct wil6210_priv *wil = wdev_to_wil(wdev); 2612e00243faSLior David struct wil6210_vif *vif = wdev_to_vif(wil, wdev); 26130216a895SLior David int rc; 26140216a895SLior David struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; 26150216a895SLior David u16 sector_index; 26160216a895SLior David u8 sector_type, mac_addr[ETH_ALEN], i; 26170216a895SLior David int cid = 0; 26180216a895SLior David 26190216a895SLior David if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) 26200216a895SLior David return -EOPNOTSUPP; 26210216a895SLior David 26220216a895SLior David rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, 26230216a895SLior David wil_rf_sector_policy, NULL); 26240216a895SLior David if (rc) { 26250216a895SLior David wil_err(wil, "Invalid rf sector ATTR\n"); 26260216a895SLior David return rc; 26270216a895SLior David } 26280216a895SLior David 26290216a895SLior David if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || 26300216a895SLior David !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { 26310216a895SLior David wil_err(wil, "Invalid rf sector spec\n"); 26320216a895SLior David return -EINVAL; 26330216a895SLior David } 26340216a895SLior David 26350216a895SLior David sector_index = nla_get_u16( 26360216a895SLior David tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); 26370216a895SLior David if (sector_index >= WIL_MAX_RF_SECTORS && 26380216a895SLior David sector_index != WMI_INVALID_RF_SECTOR_INDEX) { 26390216a895SLior David wil_err(wil, "Invalid sector index %d\n", sector_index); 26400216a895SLior David return -EINVAL; 26410216a895SLior David } 26420216a895SLior David 26430216a895SLior David sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); 26440216a895SLior David if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { 26450216a895SLior David wil_err(wil, "Invalid sector type %d\n", sector_type); 26460216a895SLior David return -EINVAL; 26470216a895SLior David } 26480216a895SLior David 26490216a895SLior David if (tb[QCA_ATTR_MAC_ADDR]) { 26500216a895SLior David ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); 26510216a895SLior David if (!is_broadcast_ether_addr(mac_addr)) { 2652e00243faSLior David cid = wil_find_cid(wil, vif->mid, mac_addr); 26530216a895SLior David if (cid < 0) { 26540216a895SLior David wil_err(wil, "invalid MAC address %pM\n", 26550216a895SLior David mac_addr); 26560216a895SLior David return -ENOENT; 26570216a895SLior David } 26580216a895SLior David } else { 26590216a895SLior David if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) { 26600216a895SLior David wil_err(wil, "broadcast MAC valid only with unlocking\n"); 26610216a895SLior David return -EINVAL; 26620216a895SLior David } 26630216a895SLior David cid = -1; 26640216a895SLior David } 26650216a895SLior David } else { 26665bd60982SLior David if (test_bit(wil_vif_fwconnected, vif->status)) { 26670216a895SLior David wil_err(wil, "must specify MAC address when connected\n"); 26680216a895SLior David return -EINVAL; 26690216a895SLior David } 26700216a895SLior David /* otherwise, using cid=0 for unassociated station */ 26710216a895SLior David } 26720216a895SLior David 26730216a895SLior David if (cid >= 0) { 2674e00243faSLior David rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index, 26750216a895SLior David sector_type, cid); 26760216a895SLior David } else { 26770216a895SLior David /* unlock all cids */ 26780216a895SLior David rc = wil_rf_sector_wmi_set_selected( 2679e00243faSLior David wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX, 2680e00243faSLior David sector_type, WIL_CID_ALL); 26810216a895SLior David if (rc == -EINVAL) { 26820216a895SLior David for (i = 0; i < WIL6210_MAX_CID; i++) { 2683e00243faSLior David if (wil->sta[i].mid != vif->mid) 2684e00243faSLior David continue; 26850216a895SLior David rc = wil_rf_sector_wmi_set_selected( 2686e00243faSLior David wil, vif->mid, 2687e00243faSLior David WMI_INVALID_RF_SECTOR_INDEX, 26880216a895SLior David sector_type, i); 26890216a895SLior David /* the FW will silently ignore and return 26900216a895SLior David * success for unused cid, so abort the loop 26910216a895SLior David * on any other error 26920216a895SLior David */ 26930216a895SLior David if (rc) { 26940216a895SLior David wil_err(wil, "unlock cid %d failed with status %d\n", 26950216a895SLior David i, rc); 26960216a895SLior David break; 26970216a895SLior David } 26980216a895SLior David } 26990216a895SLior David } 27000216a895SLior David } 27010216a895SLior David 27020216a895SLior David return rc; 27030216a895SLior David } 2704