198f44cb0SIgor Mitsyanko /* 298f44cb0SIgor Mitsyanko * Copyright (c) 2012-2012 Quantenna Communications, Inc. 398f44cb0SIgor Mitsyanko * All rights reserved. 498f44cb0SIgor Mitsyanko * 598f44cb0SIgor Mitsyanko * This program is free software; you can redistribute it and/or 698f44cb0SIgor Mitsyanko * modify it under the terms of the GNU General Public License 798f44cb0SIgor Mitsyanko * as published by the Free Software Foundation; either version 2 898f44cb0SIgor Mitsyanko * of the License, or (at your option) any later version. 998f44cb0SIgor Mitsyanko * 1098f44cb0SIgor Mitsyanko * This program is distributed in the hope that it will be useful, 1198f44cb0SIgor Mitsyanko * but WITHOUT ANY WARRANTY; without even the implied warranty of 1298f44cb0SIgor Mitsyanko * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1398f44cb0SIgor Mitsyanko * GNU General Public License for more details. 1498f44cb0SIgor Mitsyanko * 1598f44cb0SIgor Mitsyanko */ 1698f44cb0SIgor Mitsyanko 1798f44cb0SIgor Mitsyanko #include <linux/kernel.h> 1898f44cb0SIgor Mitsyanko #include <linux/etherdevice.h> 1998f44cb0SIgor Mitsyanko #include <linux/vmalloc.h> 2098f44cb0SIgor Mitsyanko #include <linux/ieee80211.h> 2198f44cb0SIgor Mitsyanko #include <net/cfg80211.h> 2298f44cb0SIgor Mitsyanko #include <net/netlink.h> 2398f44cb0SIgor Mitsyanko 2498f44cb0SIgor Mitsyanko #include "cfg80211.h" 2598f44cb0SIgor Mitsyanko #include "commands.h" 2698f44cb0SIgor Mitsyanko #include "core.h" 2798f44cb0SIgor Mitsyanko #include "util.h" 2898f44cb0SIgor Mitsyanko #include "bus.h" 2998f44cb0SIgor Mitsyanko 3098f44cb0SIgor Mitsyanko /* Supported rates to be advertised to the cfg80211 */ 3198f44cb0SIgor Mitsyanko static struct ieee80211_rate qtnf_rates_2g[] = { 3298f44cb0SIgor Mitsyanko {.bitrate = 10, .hw_value = 2, }, 3398f44cb0SIgor Mitsyanko {.bitrate = 20, .hw_value = 4, }, 3498f44cb0SIgor Mitsyanko {.bitrate = 55, .hw_value = 11, }, 3598f44cb0SIgor Mitsyanko {.bitrate = 110, .hw_value = 22, }, 3698f44cb0SIgor Mitsyanko {.bitrate = 60, .hw_value = 12, }, 3798f44cb0SIgor Mitsyanko {.bitrate = 90, .hw_value = 18, }, 3898f44cb0SIgor Mitsyanko {.bitrate = 120, .hw_value = 24, }, 3998f44cb0SIgor Mitsyanko {.bitrate = 180, .hw_value = 36, }, 4098f44cb0SIgor Mitsyanko {.bitrate = 240, .hw_value = 48, }, 4198f44cb0SIgor Mitsyanko {.bitrate = 360, .hw_value = 72, }, 4298f44cb0SIgor Mitsyanko {.bitrate = 480, .hw_value = 96, }, 4398f44cb0SIgor Mitsyanko {.bitrate = 540, .hw_value = 108, }, 4498f44cb0SIgor Mitsyanko }; 4598f44cb0SIgor Mitsyanko 4698f44cb0SIgor Mitsyanko /* Supported rates to be advertised to the cfg80211 */ 4798f44cb0SIgor Mitsyanko static struct ieee80211_rate qtnf_rates_5g[] = { 4898f44cb0SIgor Mitsyanko {.bitrate = 60, .hw_value = 12, }, 4998f44cb0SIgor Mitsyanko {.bitrate = 90, .hw_value = 18, }, 5098f44cb0SIgor Mitsyanko {.bitrate = 120, .hw_value = 24, }, 5198f44cb0SIgor Mitsyanko {.bitrate = 180, .hw_value = 36, }, 5298f44cb0SIgor Mitsyanko {.bitrate = 240, .hw_value = 48, }, 5398f44cb0SIgor Mitsyanko {.bitrate = 360, .hw_value = 72, }, 5498f44cb0SIgor Mitsyanko {.bitrate = 480, .hw_value = 96, }, 5598f44cb0SIgor Mitsyanko {.bitrate = 540, .hw_value = 108, }, 5698f44cb0SIgor Mitsyanko }; 5798f44cb0SIgor Mitsyanko 5898f44cb0SIgor Mitsyanko /* Supported crypto cipher suits to be advertised to cfg80211 */ 5998f44cb0SIgor Mitsyanko static const u32 qtnf_cipher_suites[] = { 6098f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_TKIP, 6198f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_CCMP, 6298f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_AES_CMAC, 6398f44cb0SIgor Mitsyanko }; 6498f44cb0SIgor Mitsyanko 6598f44cb0SIgor Mitsyanko /* Supported mgmt frame types to be advertised to cfg80211 */ 6698f44cb0SIgor Mitsyanko static const struct ieee80211_txrx_stypes 6798f44cb0SIgor Mitsyanko qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = { 6898f44cb0SIgor Mitsyanko [NL80211_IFTYPE_STATION] = { 6998f44cb0SIgor Mitsyanko .tx = BIT(IEEE80211_STYPE_ACTION >> 4), 7098f44cb0SIgor Mitsyanko .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 7198f44cb0SIgor Mitsyanko BIT(IEEE80211_STYPE_PROBE_REQ >> 4), 7298f44cb0SIgor Mitsyanko }, 7398f44cb0SIgor Mitsyanko [NL80211_IFTYPE_AP] = { 7498f44cb0SIgor Mitsyanko .tx = BIT(IEEE80211_STYPE_ACTION >> 4), 7598f44cb0SIgor Mitsyanko .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 7698f44cb0SIgor Mitsyanko BIT(IEEE80211_STYPE_PROBE_REQ >> 4), 7798f44cb0SIgor Mitsyanko }, 7898f44cb0SIgor Mitsyanko }; 7998f44cb0SIgor Mitsyanko 8098f44cb0SIgor Mitsyanko static int 8198f44cb0SIgor Mitsyanko qtnf_change_virtual_intf(struct wiphy *wiphy, 8298f44cb0SIgor Mitsyanko struct net_device *dev, 8398f44cb0SIgor Mitsyanko enum nl80211_iftype type, 8498f44cb0SIgor Mitsyanko struct vif_params *params) 8598f44cb0SIgor Mitsyanko { 8698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 8798f44cb0SIgor Mitsyanko u8 *mac_addr; 8898f44cb0SIgor Mitsyanko int ret; 8998f44cb0SIgor Mitsyanko 9098f44cb0SIgor Mitsyanko if (params) 9198f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 9298f44cb0SIgor Mitsyanko else 9398f44cb0SIgor Mitsyanko mac_addr = NULL; 9498f44cb0SIgor Mitsyanko 9598f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 9698f44cb0SIgor Mitsyanko 9798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_intf_type(vif, type, mac_addr); 9898f44cb0SIgor Mitsyanko if (ret) { 9998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change VIF type: %d\n", 10098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, ret); 10198f44cb0SIgor Mitsyanko return ret; 10298f44cb0SIgor Mitsyanko } 10398f44cb0SIgor Mitsyanko 10498f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 10598f44cb0SIgor Mitsyanko return 0; 10698f44cb0SIgor Mitsyanko } 10798f44cb0SIgor Mitsyanko 10898f44cb0SIgor Mitsyanko int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) 10998f44cb0SIgor Mitsyanko { 11098f44cb0SIgor Mitsyanko struct net_device *netdev = wdev->netdev; 11198f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 11298f44cb0SIgor Mitsyanko 11398f44cb0SIgor Mitsyanko if (WARN_ON(!netdev)) 11498f44cb0SIgor Mitsyanko return -EFAULT; 11598f44cb0SIgor Mitsyanko 11698f44cb0SIgor Mitsyanko vif = qtnf_netdev_get_priv(wdev->netdev); 11798f44cb0SIgor Mitsyanko 11898f44cb0SIgor Mitsyanko if (qtnf_cmd_send_del_intf(vif)) 11998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, 12098f44cb0SIgor Mitsyanko vif->vifid); 12198f44cb0SIgor Mitsyanko 12298f44cb0SIgor Mitsyanko /* Stop data */ 12398f44cb0SIgor Mitsyanko netif_tx_stop_all_queues(netdev); 12498f44cb0SIgor Mitsyanko if (netif_carrier_ok(netdev)) 12598f44cb0SIgor Mitsyanko netif_carrier_off(netdev); 12698f44cb0SIgor Mitsyanko 12798f44cb0SIgor Mitsyanko if (netdev->reg_state == NETREG_REGISTERED) 12898f44cb0SIgor Mitsyanko unregister_netdevice(netdev); 12998f44cb0SIgor Mitsyanko 13098f44cb0SIgor Mitsyanko vif->netdev->ieee80211_ptr = NULL; 13198f44cb0SIgor Mitsyanko vif->netdev = NULL; 13298f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 13398f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 1349766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 13598f44cb0SIgor Mitsyanko 13698f44cb0SIgor Mitsyanko return 0; 13798f44cb0SIgor Mitsyanko } 13898f44cb0SIgor Mitsyanko 13998f44cb0SIgor Mitsyanko static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, 14098f44cb0SIgor Mitsyanko const char *name, 14198f44cb0SIgor Mitsyanko unsigned char name_assign_t, 14298f44cb0SIgor Mitsyanko enum nl80211_iftype type, 14398f44cb0SIgor Mitsyanko struct vif_params *params) 14498f44cb0SIgor Mitsyanko { 14598f44cb0SIgor Mitsyanko struct qtnf_wmac *mac; 14698f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 14798f44cb0SIgor Mitsyanko u8 *mac_addr = NULL; 14898f44cb0SIgor Mitsyanko 14998f44cb0SIgor Mitsyanko mac = wiphy_priv(wiphy); 15098f44cb0SIgor Mitsyanko 15198f44cb0SIgor Mitsyanko if (!mac) 15298f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 15398f44cb0SIgor Mitsyanko 15498f44cb0SIgor Mitsyanko switch (type) { 15598f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 15698f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 15798f44cb0SIgor Mitsyanko vif = qtnf_mac_get_free_vif(mac); 15898f44cb0SIgor Mitsyanko if (!vif) { 15998f44cb0SIgor Mitsyanko pr_err("MAC%u: no free VIF available\n", mac->macid); 16098f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 16198f44cb0SIgor Mitsyanko } 16298f44cb0SIgor Mitsyanko 16398f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 16498f44cb0SIgor Mitsyanko vif->bss_priority = QTNF_DEF_BSS_PRIORITY; 16598f44cb0SIgor Mitsyanko vif->wdev.wiphy = wiphy; 16698f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 16798f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 16898f44cb0SIgor Mitsyanko break; 16998f44cb0SIgor Mitsyanko default: 17098f44cb0SIgor Mitsyanko pr_err("MAC%u: unsupported IF type %d\n", mac->macid, type); 17198f44cb0SIgor Mitsyanko return ERR_PTR(-ENOTSUPP); 17298f44cb0SIgor Mitsyanko } 17398f44cb0SIgor Mitsyanko 17498f44cb0SIgor Mitsyanko if (params) 17598f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 17698f44cb0SIgor Mitsyanko 17798f44cb0SIgor Mitsyanko if (qtnf_cmd_send_add_intf(vif, type, mac_addr)) { 17898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add VIF\n", mac->macid, vif->vifid); 17998f44cb0SIgor Mitsyanko goto err_cmd; 18098f44cb0SIgor Mitsyanko } 18198f44cb0SIgor Mitsyanko 18298f44cb0SIgor Mitsyanko if (!is_valid_ether_addr(vif->mac_addr)) { 18398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: FW reported bad MAC: %pM\n", 18498f44cb0SIgor Mitsyanko mac->macid, vif->vifid, vif->mac_addr); 18598f44cb0SIgor Mitsyanko goto err_mac; 18698f44cb0SIgor Mitsyanko } 18798f44cb0SIgor Mitsyanko 18898f44cb0SIgor Mitsyanko if (qtnf_core_net_attach(mac, vif, name, name_assign_t, type)) { 18998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid, 19098f44cb0SIgor Mitsyanko vif->vifid); 19198f44cb0SIgor Mitsyanko goto err_net; 19298f44cb0SIgor Mitsyanko } 19398f44cb0SIgor Mitsyanko 19498f44cb0SIgor Mitsyanko vif->wdev.netdev = vif->netdev; 19598f44cb0SIgor Mitsyanko return &vif->wdev; 19698f44cb0SIgor Mitsyanko 19798f44cb0SIgor Mitsyanko err_net: 19898f44cb0SIgor Mitsyanko vif->netdev = NULL; 19998f44cb0SIgor Mitsyanko err_mac: 20098f44cb0SIgor Mitsyanko qtnf_cmd_send_del_intf(vif); 20198f44cb0SIgor Mitsyanko err_cmd: 20298f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 2039766d1ddSIgor Mitsyanko eth_zero_addr(vif->mac_addr); 2049766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 20598f44cb0SIgor Mitsyanko 20698f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 20798f44cb0SIgor Mitsyanko } 20898f44cb0SIgor Mitsyanko 20998f44cb0SIgor Mitsyanko static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, 21098f44cb0SIgor Mitsyanko const struct cfg80211_beacon_data *info) 21198f44cb0SIgor Mitsyanko { 21298f44cb0SIgor Mitsyanko int ret = 0; 21398f44cb0SIgor Mitsyanko 21498f44cb0SIgor Mitsyanko if (!info->beacon_ies || !info->beacon_ies_len) { 21598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, 21698f44cb0SIgor Mitsyanko NULL, 0); 21798f44cb0SIgor Mitsyanko } else { 21898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, 21998f44cb0SIgor Mitsyanko info->beacon_ies, 22098f44cb0SIgor Mitsyanko info->beacon_ies_len); 22198f44cb0SIgor Mitsyanko } 22298f44cb0SIgor Mitsyanko 22398f44cb0SIgor Mitsyanko if (ret) 22498f44cb0SIgor Mitsyanko goto out; 22598f44cb0SIgor Mitsyanko 22698f44cb0SIgor Mitsyanko if (!info->proberesp_ies || !info->proberesp_ies_len) { 22798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 22898f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_PROBE_RESP, 22998f44cb0SIgor Mitsyanko NULL, 0); 23098f44cb0SIgor Mitsyanko } else { 23198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 23298f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_PROBE_RESP, 23398f44cb0SIgor Mitsyanko info->proberesp_ies, 23498f44cb0SIgor Mitsyanko info->proberesp_ies_len); 23598f44cb0SIgor Mitsyanko } 23698f44cb0SIgor Mitsyanko 23798f44cb0SIgor Mitsyanko if (ret) 23898f44cb0SIgor Mitsyanko goto out; 23998f44cb0SIgor Mitsyanko 24098f44cb0SIgor Mitsyanko if (!info->assocresp_ies || !info->assocresp_ies_len) { 24198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 24298f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_ASSOC_RESP, 24398f44cb0SIgor Mitsyanko NULL, 0); 24498f44cb0SIgor Mitsyanko } else { 24598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 24698f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_ASSOC_RESP, 24798f44cb0SIgor Mitsyanko info->assocresp_ies, 24898f44cb0SIgor Mitsyanko info->assocresp_ies_len); 24998f44cb0SIgor Mitsyanko } 25098f44cb0SIgor Mitsyanko 25198f44cb0SIgor Mitsyanko out: 25298f44cb0SIgor Mitsyanko return ret; 25398f44cb0SIgor Mitsyanko } 25498f44cb0SIgor Mitsyanko 25598f44cb0SIgor Mitsyanko static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, 25698f44cb0SIgor Mitsyanko struct cfg80211_beacon_data *info) 25798f44cb0SIgor Mitsyanko { 25898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 25998f44cb0SIgor Mitsyanko 26098f44cb0SIgor Mitsyanko return qtnf_mgmt_set_appie(vif, info); 26198f44cb0SIgor Mitsyanko } 26298f44cb0SIgor Mitsyanko 26398f44cb0SIgor Mitsyanko static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, 26498f44cb0SIgor Mitsyanko struct cfg80211_ap_settings *settings) 26598f44cb0SIgor Mitsyanko { 26698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 26798f44cb0SIgor Mitsyanko int ret; 26898f44cb0SIgor Mitsyanko 2699b692df1SIgor Mitsyanko ret = qtnf_cmd_send_config_ap(vif, settings); 27098f44cb0SIgor Mitsyanko if (ret) { 27198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to push config to FW\n", 27298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 27398f44cb0SIgor Mitsyanko goto out; 27498f44cb0SIgor Mitsyanko } 27598f44cb0SIgor Mitsyanko 27698f44cb0SIgor Mitsyanko ret = qtnf_mgmt_set_appie(vif, &settings->beacon); 27798f44cb0SIgor Mitsyanko if (ret) { 27898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add IEs to beacon\n", 27998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 28098f44cb0SIgor Mitsyanko goto out; 28198f44cb0SIgor Mitsyanko } 28298f44cb0SIgor Mitsyanko 28398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_start_ap(vif); 284d7b80052SIgor Mitsyanko if (ret) 28598f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, 28698f44cb0SIgor Mitsyanko vif->vifid); 28798f44cb0SIgor Mitsyanko 28898f44cb0SIgor Mitsyanko out: 28998f44cb0SIgor Mitsyanko return ret; 29098f44cb0SIgor Mitsyanko } 29198f44cb0SIgor Mitsyanko 29298f44cb0SIgor Mitsyanko static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) 29398f44cb0SIgor Mitsyanko { 29498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 29598f44cb0SIgor Mitsyanko int ret; 29698f44cb0SIgor Mitsyanko 29798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_stop_ap(vif); 29898f44cb0SIgor Mitsyanko if (ret) { 29998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to stop AP operation in FW\n", 30098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 30198f44cb0SIgor Mitsyanko 30298f44cb0SIgor Mitsyanko netif_carrier_off(vif->netdev); 30398f44cb0SIgor Mitsyanko } 30498f44cb0SIgor Mitsyanko 30598f44cb0SIgor Mitsyanko return ret; 30698f44cb0SIgor Mitsyanko } 30798f44cb0SIgor Mitsyanko 30898f44cb0SIgor Mitsyanko static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed) 30998f44cb0SIgor Mitsyanko { 31098f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 31198f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 31298f44cb0SIgor Mitsyanko int ret; 31398f44cb0SIgor Mitsyanko 31498f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 31598f44cb0SIgor Mitsyanko if (!vif) { 31698f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 31798f44cb0SIgor Mitsyanko return -EFAULT; 31898f44cb0SIgor Mitsyanko } 31998f44cb0SIgor Mitsyanko 32098f44cb0SIgor Mitsyanko if (changed & (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) { 32198f44cb0SIgor Mitsyanko pr_err("MAC%u: can't modify retry params\n", mac->macid); 32298f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 32398f44cb0SIgor Mitsyanko } 32498f44cb0SIgor Mitsyanko 32598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_update_phy_params(mac, changed); 32698f44cb0SIgor Mitsyanko if (ret) 32798f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to update PHY params\n", mac->macid); 32898f44cb0SIgor Mitsyanko 32998f44cb0SIgor Mitsyanko return ret; 33098f44cb0SIgor Mitsyanko } 33198f44cb0SIgor Mitsyanko 33298f44cb0SIgor Mitsyanko static void 33398f44cb0SIgor Mitsyanko qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, 33498f44cb0SIgor Mitsyanko u16 frame_type, bool reg) 33598f44cb0SIgor Mitsyanko { 33698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 33798f44cb0SIgor Mitsyanko u16 mgmt_type; 33898f44cb0SIgor Mitsyanko u16 new_mask; 33998f44cb0SIgor Mitsyanko u16 qlink_frame_type = 0; 34098f44cb0SIgor Mitsyanko 34198f44cb0SIgor Mitsyanko mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; 34298f44cb0SIgor Mitsyanko 34398f44cb0SIgor Mitsyanko if (reg) 34498f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask | BIT(mgmt_type); 34598f44cb0SIgor Mitsyanko else 34698f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask & ~BIT(mgmt_type); 34798f44cb0SIgor Mitsyanko 34898f44cb0SIgor Mitsyanko if (new_mask == vif->mgmt_frames_bitmask) 34998f44cb0SIgor Mitsyanko return; 35098f44cb0SIgor Mitsyanko 35198f44cb0SIgor Mitsyanko switch (frame_type & IEEE80211_FCTL_STYPE) { 35298f44cb0SIgor Mitsyanko case IEEE80211_STYPE_PROBE_REQ: 35398f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ; 35498f44cb0SIgor Mitsyanko break; 35598f44cb0SIgor Mitsyanko case IEEE80211_STYPE_ACTION: 35698f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_ACTION; 35798f44cb0SIgor Mitsyanko break; 35898f44cb0SIgor Mitsyanko default: 35998f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported frame type: %X\n", 36098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, 36198f44cb0SIgor Mitsyanko (frame_type & IEEE80211_FCTL_STYPE) >> 4); 36298f44cb0SIgor Mitsyanko return; 36398f44cb0SIgor Mitsyanko } 36498f44cb0SIgor Mitsyanko 36598f44cb0SIgor Mitsyanko if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) { 36698f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: failed to %sregister mgmt frame type 0x%x\n", 36798f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", 36898f44cb0SIgor Mitsyanko frame_type); 36998f44cb0SIgor Mitsyanko return; 37098f44cb0SIgor Mitsyanko } 37198f44cb0SIgor Mitsyanko 37298f44cb0SIgor Mitsyanko vif->mgmt_frames_bitmask = new_mask; 37398f44cb0SIgor Mitsyanko pr_debug("VIF%u.%u: %sregistered mgmt frame type 0x%x\n", 37498f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", frame_type); 37598f44cb0SIgor Mitsyanko } 37698f44cb0SIgor Mitsyanko 37798f44cb0SIgor Mitsyanko static int 37898f44cb0SIgor Mitsyanko qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 37998f44cb0SIgor Mitsyanko struct cfg80211_mgmt_tx_params *params, u64 *cookie) 38098f44cb0SIgor Mitsyanko { 38198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 38298f44cb0SIgor Mitsyanko const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; 38398f44cb0SIgor Mitsyanko u32 short_cookie = prandom_u32(); 38498f44cb0SIgor Mitsyanko u16 flags = 0; 38598f44cb0SIgor Mitsyanko 38698f44cb0SIgor Mitsyanko *cookie = short_cookie; 38798f44cb0SIgor Mitsyanko 38898f44cb0SIgor Mitsyanko if (params->offchan) 38998f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN; 39098f44cb0SIgor Mitsyanko 39198f44cb0SIgor Mitsyanko if (params->no_cck) 39298f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_NO_CCK; 39398f44cb0SIgor Mitsyanko 39498f44cb0SIgor Mitsyanko if (params->dont_wait_for_ack) 39598f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT; 39698f44cb0SIgor Mitsyanko 39798f44cb0SIgor Mitsyanko pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n", 39898f44cb0SIgor Mitsyanko wdev->netdev->name, params->chan->center_freq, 39998f44cb0SIgor Mitsyanko le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da, 40098f44cb0SIgor Mitsyanko params->len, short_cookie, flags); 40198f44cb0SIgor Mitsyanko 40298f44cb0SIgor Mitsyanko return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags, 40398f44cb0SIgor Mitsyanko params->chan->center_freq, 40498f44cb0SIgor Mitsyanko params->buf, params->len); 40598f44cb0SIgor Mitsyanko } 40698f44cb0SIgor Mitsyanko 40798f44cb0SIgor Mitsyanko static int 40898f44cb0SIgor Mitsyanko qtnf_get_station(struct wiphy *wiphy, struct net_device *dev, 40998f44cb0SIgor Mitsyanko const u8 *mac, struct station_info *sinfo) 41098f44cb0SIgor Mitsyanko { 41198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 41298f44cb0SIgor Mitsyanko 41398f44cb0SIgor Mitsyanko return qtnf_cmd_get_sta_info(vif, mac, sinfo); 41498f44cb0SIgor Mitsyanko } 41598f44cb0SIgor Mitsyanko 41698f44cb0SIgor Mitsyanko static int 41798f44cb0SIgor Mitsyanko qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev, 41898f44cb0SIgor Mitsyanko int idx, u8 *mac, struct station_info *sinfo) 41998f44cb0SIgor Mitsyanko { 42098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 42198f44cb0SIgor Mitsyanko const struct qtnf_sta_node *sta_node; 42298f44cb0SIgor Mitsyanko int ret; 42398f44cb0SIgor Mitsyanko 42498f44cb0SIgor Mitsyanko sta_node = qtnf_sta_list_lookup_index(&vif->sta_list, idx); 42598f44cb0SIgor Mitsyanko 42698f44cb0SIgor Mitsyanko if (unlikely(!sta_node)) 42798f44cb0SIgor Mitsyanko return -ENOENT; 42898f44cb0SIgor Mitsyanko 42998f44cb0SIgor Mitsyanko ether_addr_copy(mac, sta_node->mac_addr); 43098f44cb0SIgor Mitsyanko 43198f44cb0SIgor Mitsyanko ret = qtnf_cmd_get_sta_info(vif, sta_node->mac_addr, sinfo); 43298f44cb0SIgor Mitsyanko 43398f44cb0SIgor Mitsyanko if (unlikely(ret == -ENOENT)) { 43498f44cb0SIgor Mitsyanko qtnf_sta_list_del(&vif->sta_list, mac); 43598f44cb0SIgor Mitsyanko cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL); 43698f44cb0SIgor Mitsyanko sinfo->filled = 0; 43798f44cb0SIgor Mitsyanko } 43898f44cb0SIgor Mitsyanko 43998f44cb0SIgor Mitsyanko return ret; 44098f44cb0SIgor Mitsyanko } 44198f44cb0SIgor Mitsyanko 44298f44cb0SIgor Mitsyanko static int qtnf_add_key(struct wiphy *wiphy, struct net_device *dev, 44398f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr, 44498f44cb0SIgor Mitsyanko struct key_params *params) 44598f44cb0SIgor Mitsyanko { 44698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 44798f44cb0SIgor Mitsyanko int ret; 44898f44cb0SIgor Mitsyanko 44998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params); 45098f44cb0SIgor Mitsyanko if (ret) 45198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add key: cipher=%x idx=%u pw=%u\n", 45298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->cipher, key_index, 45398f44cb0SIgor Mitsyanko pairwise); 45498f44cb0SIgor Mitsyanko 45598f44cb0SIgor Mitsyanko return ret; 45698f44cb0SIgor Mitsyanko } 45798f44cb0SIgor Mitsyanko 45898f44cb0SIgor Mitsyanko static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev, 45998f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr) 46098f44cb0SIgor Mitsyanko { 46198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 46298f44cb0SIgor Mitsyanko int ret; 46398f44cb0SIgor Mitsyanko 46498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr); 46598f44cb0SIgor Mitsyanko if (ret) 46698f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n", 46798f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, pairwise); 46898f44cb0SIgor Mitsyanko 46998f44cb0SIgor Mitsyanko return ret; 47098f44cb0SIgor Mitsyanko } 47198f44cb0SIgor Mitsyanko 47298f44cb0SIgor Mitsyanko static int qtnf_set_default_key(struct wiphy *wiphy, struct net_device *dev, 47398f44cb0SIgor Mitsyanko u8 key_index, bool unicast, bool multicast) 47498f44cb0SIgor Mitsyanko { 47598f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 47698f44cb0SIgor Mitsyanko int ret; 47798f44cb0SIgor Mitsyanko 47898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_key(vif, key_index, unicast, multicast); 47998f44cb0SIgor Mitsyanko if (ret) 48098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set dflt key: idx=%u uc=%u mc=%u\n", 48198f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, unicast, 48298f44cb0SIgor Mitsyanko multicast); 48398f44cb0SIgor Mitsyanko 48498f44cb0SIgor Mitsyanko return ret; 48598f44cb0SIgor Mitsyanko } 48698f44cb0SIgor Mitsyanko 48798f44cb0SIgor Mitsyanko static int 48898f44cb0SIgor Mitsyanko qtnf_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, 48998f44cb0SIgor Mitsyanko u8 key_index) 49098f44cb0SIgor Mitsyanko { 49198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 49298f44cb0SIgor Mitsyanko int ret; 49398f44cb0SIgor Mitsyanko 49498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_mgmt_key(vif, key_index); 49598f44cb0SIgor Mitsyanko if (ret) 49698f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set default MGMT key: idx=%u\n", 49798f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index); 49898f44cb0SIgor Mitsyanko 49998f44cb0SIgor Mitsyanko return ret; 50098f44cb0SIgor Mitsyanko } 50198f44cb0SIgor Mitsyanko 50298f44cb0SIgor Mitsyanko static int 50398f44cb0SIgor Mitsyanko qtnf_change_station(struct wiphy *wiphy, struct net_device *dev, 50498f44cb0SIgor Mitsyanko const u8 *mac, struct station_parameters *params) 50598f44cb0SIgor Mitsyanko { 50698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 50798f44cb0SIgor Mitsyanko int ret; 50898f44cb0SIgor Mitsyanko 50998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_sta(vif, mac, params); 51098f44cb0SIgor Mitsyanko if (ret) 51198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change STA %pM\n", 51298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, mac); 51398f44cb0SIgor Mitsyanko 51498f44cb0SIgor Mitsyanko return ret; 51598f44cb0SIgor Mitsyanko } 51698f44cb0SIgor Mitsyanko 51798f44cb0SIgor Mitsyanko static int 51898f44cb0SIgor Mitsyanko qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, 51998f44cb0SIgor Mitsyanko struct station_del_parameters *params) 52098f44cb0SIgor Mitsyanko { 52198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 52298f44cb0SIgor Mitsyanko int ret; 52398f44cb0SIgor Mitsyanko 52498f44cb0SIgor Mitsyanko if (params->mac && 52598f44cb0SIgor Mitsyanko (vif->wdev.iftype == NL80211_IFTYPE_AP) && 52698f44cb0SIgor Mitsyanko !is_broadcast_ether_addr(params->mac) && 52798f44cb0SIgor Mitsyanko !qtnf_sta_list_lookup(&vif->sta_list, params->mac)) 52898f44cb0SIgor Mitsyanko return 0; 52998f44cb0SIgor Mitsyanko 53098f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 53198f44cb0SIgor Mitsyanko 53298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_sta(vif, params); 53398f44cb0SIgor Mitsyanko if (ret) 53498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete STA %pM\n", 53598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->mac); 53698f44cb0SIgor Mitsyanko return ret; 53798f44cb0SIgor Mitsyanko } 53898f44cb0SIgor Mitsyanko 539c7ead2abSSergey Matyukevich static void qtnf_scan_timeout(unsigned long data) 540c7ead2abSSergey Matyukevich { 541c7ead2abSSergey Matyukevich struct qtnf_wmac *mac = (struct qtnf_wmac *)data; 542c7ead2abSSergey Matyukevich 543c7ead2abSSergey Matyukevich pr_warn("mac%d scan timed out\n", mac->macid); 544c7ead2abSSergey Matyukevich qtnf_scan_done(mac, true); 545c7ead2abSSergey Matyukevich } 546c7ead2abSSergey Matyukevich 54798f44cb0SIgor Mitsyanko static int 54898f44cb0SIgor Mitsyanko qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) 54998f44cb0SIgor Mitsyanko { 55098f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 55198f44cb0SIgor Mitsyanko 55298f44cb0SIgor Mitsyanko mac->scan_req = request; 55398f44cb0SIgor Mitsyanko 554c7ead2abSSergey Matyukevich if (qtnf_cmd_send_scan(mac)) { 55598f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to start scan\n", mac->macid); 556c7ead2abSSergey Matyukevich mac->scan_req = NULL; 557c7ead2abSSergey Matyukevich return -EFAULT; 558c7ead2abSSergey Matyukevich } 55998f44cb0SIgor Mitsyanko 560c7ead2abSSergey Matyukevich mac->scan_timeout.data = (unsigned long)mac; 561c7ead2abSSergey Matyukevich mac->scan_timeout.function = qtnf_scan_timeout; 562c7ead2abSSergey Matyukevich mod_timer(&mac->scan_timeout, 563c7ead2abSSergey Matyukevich jiffies + QTNF_SCAN_TIMEOUT_SEC * HZ); 564c7ead2abSSergey Matyukevich 565c7ead2abSSergey Matyukevich return 0; 56698f44cb0SIgor Mitsyanko } 56798f44cb0SIgor Mitsyanko 56898f44cb0SIgor Mitsyanko static int 56998f44cb0SIgor Mitsyanko qtnf_connect(struct wiphy *wiphy, struct net_device *dev, 57098f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 57198f44cb0SIgor Mitsyanko { 57298f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 57398f44cb0SIgor Mitsyanko int ret; 57498f44cb0SIgor Mitsyanko 57598f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 57698f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 57798f44cb0SIgor Mitsyanko 57898f44cb0SIgor Mitsyanko if (vif->sta_state != QTNF_STA_DISCONNECTED) 57998f44cb0SIgor Mitsyanko return -EBUSY; 58098f44cb0SIgor Mitsyanko 58198f44cb0SIgor Mitsyanko if (sme->bssid) 5829766d1ddSIgor Mitsyanko ether_addr_copy(vif->bssid, sme->bssid); 58398f44cb0SIgor Mitsyanko else 5849766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 58598f44cb0SIgor Mitsyanko 58698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_connect(vif, sme); 58798f44cb0SIgor Mitsyanko if (ret) { 58898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to connect\n", vif->mac->macid, 58998f44cb0SIgor Mitsyanko vif->vifid); 59098f44cb0SIgor Mitsyanko return ret; 59198f44cb0SIgor Mitsyanko } 59298f44cb0SIgor Mitsyanko 59398f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_CONNECTING; 59498f44cb0SIgor Mitsyanko return 0; 59598f44cb0SIgor Mitsyanko } 59698f44cb0SIgor Mitsyanko 59798f44cb0SIgor Mitsyanko static int 59898f44cb0SIgor Mitsyanko qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, 59998f44cb0SIgor Mitsyanko u16 reason_code) 60098f44cb0SIgor Mitsyanko { 60198f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 60298f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 60398f44cb0SIgor Mitsyanko int ret; 60498f44cb0SIgor Mitsyanko 60598f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 60698f44cb0SIgor Mitsyanko if (!vif) { 60798f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 60898f44cb0SIgor Mitsyanko return -EFAULT; 60998f44cb0SIgor Mitsyanko } 61098f44cb0SIgor Mitsyanko 61198f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 61298f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 61398f44cb0SIgor Mitsyanko 61498f44cb0SIgor Mitsyanko if (vif->sta_state == QTNF_STA_DISCONNECTED) 61598f44cb0SIgor Mitsyanko return 0; 61698f44cb0SIgor Mitsyanko 61798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_disconnect(vif, reason_code); 61898f44cb0SIgor Mitsyanko if (ret) { 61998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to disconnect\n", mac->macid, 62098f44cb0SIgor Mitsyanko vif->vifid); 62198f44cb0SIgor Mitsyanko return ret; 62298f44cb0SIgor Mitsyanko } 62398f44cb0SIgor Mitsyanko 62498f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 62598f44cb0SIgor Mitsyanko return 0; 62698f44cb0SIgor Mitsyanko } 62798f44cb0SIgor Mitsyanko 6287c04b439SSergey Matyukevich static int 6297c04b439SSergey Matyukevich qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, 6307c04b439SSergey Matyukevich int idx, struct survey_info *survey) 6317c04b439SSergey Matyukevich { 6327c04b439SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 6337c04b439SSergey Matyukevich struct ieee80211_supported_band *sband; 63434f1145bSSergey Matyukevich struct cfg80211_chan_def *chandef; 6357c04b439SSergey Matyukevich struct ieee80211_channel *chan; 6367c04b439SSergey Matyukevich struct qtnf_chan_stats stats; 63727894448SSergey Matyukevich struct qtnf_vif *vif; 6387c04b439SSergey Matyukevich int ret; 6397c04b439SSergey Matyukevich 64027894448SSergey Matyukevich vif = qtnf_netdev_get_priv(dev); 64134f1145bSSergey Matyukevich chandef = &mac->chandef; 64227894448SSergey Matyukevich 6437c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_2GHZ]; 6447c04b439SSergey Matyukevich if (sband && idx >= sband->n_channels) { 6457c04b439SSergey Matyukevich idx -= sband->n_channels; 6467c04b439SSergey Matyukevich sband = NULL; 6477c04b439SSergey Matyukevich } 6487c04b439SSergey Matyukevich 6497c04b439SSergey Matyukevich if (!sband) 6507c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_5GHZ]; 6517c04b439SSergey Matyukevich 6527c04b439SSergey Matyukevich if (!sband || idx >= sband->n_channels) 6537c04b439SSergey Matyukevich return -ENOENT; 6547c04b439SSergey Matyukevich 6557c04b439SSergey Matyukevich chan = &sband->channels[idx]; 6567c04b439SSergey Matyukevich memset(&stats, 0, sizeof(stats)); 6577c04b439SSergey Matyukevich 6587c04b439SSergey Matyukevich survey->channel = chan; 6597c04b439SSergey Matyukevich survey->filled = 0x0; 6607c04b439SSergey Matyukevich 66134f1145bSSergey Matyukevich if (chandef->chan) { 66234f1145bSSergey Matyukevich if (chan->hw_value == chandef->chan->hw_value) 66334f1145bSSergey Matyukevich survey->filled = SURVEY_INFO_IN_USE; 66434f1145bSSergey Matyukevich } 66527894448SSergey Matyukevich 6667c04b439SSergey Matyukevich ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); 6677c04b439SSergey Matyukevich switch (ret) { 6687c04b439SSergey Matyukevich case 0: 6697c04b439SSergey Matyukevich if (unlikely(stats.chan_num != chan->hw_value)) { 6707c04b439SSergey Matyukevich pr_err("received stats for channel %d instead of %d\n", 6717c04b439SSergey Matyukevich stats.chan_num, chan->hw_value); 6727c04b439SSergey Matyukevich ret = -EINVAL; 6737c04b439SSergey Matyukevich break; 6747c04b439SSergey Matyukevich } 6757c04b439SSergey Matyukevich 67634f1145bSSergey Matyukevich survey->filled |= SURVEY_INFO_TIME | 6777c04b439SSergey Matyukevich SURVEY_INFO_TIME_SCAN | 6787c04b439SSergey Matyukevich SURVEY_INFO_TIME_BUSY | 6797c04b439SSergey Matyukevich SURVEY_INFO_TIME_RX | 6807c04b439SSergey Matyukevich SURVEY_INFO_TIME_TX | 6817c04b439SSergey Matyukevich SURVEY_INFO_NOISE_DBM; 6827c04b439SSergey Matyukevich 6837c04b439SSergey Matyukevich survey->time_scan = stats.cca_try; 6847c04b439SSergey Matyukevich survey->time = stats.cca_try; 6857c04b439SSergey Matyukevich survey->time_tx = stats.cca_tx; 6867c04b439SSergey Matyukevich survey->time_rx = stats.cca_rx; 6877c04b439SSergey Matyukevich survey->time_busy = stats.cca_busy; 6887c04b439SSergey Matyukevich survey->noise = stats.chan_noise; 6897c04b439SSergey Matyukevich break; 6907c04b439SSergey Matyukevich case -ENOENT: 6917c04b439SSergey Matyukevich pr_debug("no stats for channel %u\n", chan->hw_value); 6927c04b439SSergey Matyukevich ret = 0; 6937c04b439SSergey Matyukevich break; 6947c04b439SSergey Matyukevich default: 6957c04b439SSergey Matyukevich pr_debug("failed to get chan(%d) stats from card\n", 6967c04b439SSergey Matyukevich chan->hw_value); 6977c04b439SSergey Matyukevich ret = -EINVAL; 6987c04b439SSergey Matyukevich break; 6997c04b439SSergey Matyukevich } 7007c04b439SSergey Matyukevich 7017c04b439SSergey Matyukevich return ret; 7027c04b439SSergey Matyukevich } 7037c04b439SSergey Matyukevich 70427894448SSergey Matyukevich static int 70527894448SSergey Matyukevich qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, 70627894448SSergey Matyukevich struct cfg80211_chan_def *chandef) 70727894448SSergey Matyukevich { 70834f1145bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 70927894448SSergey Matyukevich struct net_device *ndev = wdev->netdev; 71027894448SSergey Matyukevich struct qtnf_vif *vif; 7119e5478b6SIgor Mitsyanko int ret; 71227894448SSergey Matyukevich 71327894448SSergey Matyukevich if (!ndev) 71427894448SSergey Matyukevich return -ENODEV; 71527894448SSergey Matyukevich 71627894448SSergey Matyukevich vif = qtnf_netdev_get_priv(wdev->netdev); 71727894448SSergey Matyukevich 7189e5478b6SIgor Mitsyanko ret = qtnf_cmd_get_channel(vif, chandef); 7199e5478b6SIgor Mitsyanko if (ret) { 7209e5478b6SIgor Mitsyanko pr_err("%s: failed to get channel: %d\n", ndev->name, ret); 7219e5478b6SIgor Mitsyanko goto out; 72227894448SSergey Matyukevich } 72327894448SSergey Matyukevich 7249e5478b6SIgor Mitsyanko if (!cfg80211_chandef_valid(chandef)) { 7259e5478b6SIgor Mitsyanko pr_err("%s: bad chan freq1=%u freq2=%u bw=%u\n", ndev->name, 7269e5478b6SIgor Mitsyanko chandef->center_freq1, chandef->center_freq2, 7279e5478b6SIgor Mitsyanko chandef->width); 7289e5478b6SIgor Mitsyanko ret = -ENODATA; 72934f1145bSSergey Matyukevich } 73034f1145bSSergey Matyukevich 7319e5478b6SIgor Mitsyanko memcpy(&mac->chandef, chandef, sizeof(mac->chandef)); 7329e5478b6SIgor Mitsyanko 7339e5478b6SIgor Mitsyanko out: 7349e5478b6SIgor Mitsyanko return ret; 73527894448SSergey Matyukevich } 73627894448SSergey Matyukevich 73797883695SSergey Matyukevich static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, 73897883695SSergey Matyukevich struct cfg80211_csa_settings *params) 73997883695SSergey Matyukevich { 74097883695SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 74197883695SSergey Matyukevich int ret; 74297883695SSergey Matyukevich 74397883695SSergey Matyukevich pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, 74497883695SSergey Matyukevich params->chandef.chan->hw_value, params->count, 74597883695SSergey Matyukevich params->radar_required, params->block_tx); 74697883695SSergey Matyukevich 74797883695SSergey Matyukevich if (!cfg80211_chandef_valid(¶ms->chandef)) { 74897883695SSergey Matyukevich pr_err("%s: invalid channel\n", dev->name); 74997883695SSergey Matyukevich return -EINVAL; 75097883695SSergey Matyukevich } 75197883695SSergey Matyukevich 7528c015b90SIgor Mitsyanko ret = qtnf_cmd_send_chan_switch(vif, params); 75397883695SSergey Matyukevich if (ret) 75497883695SSergey Matyukevich pr_warn("%s: failed to switch to channel (%u)\n", 75597883695SSergey Matyukevich dev->name, params->chandef.chan->hw_value); 75697883695SSergey Matyukevich 75797883695SSergey Matyukevich return ret; 75897883695SSergey Matyukevich } 75997883695SSergey Matyukevich 76098f44cb0SIgor Mitsyanko static struct cfg80211_ops qtn_cfg80211_ops = { 76198f44cb0SIgor Mitsyanko .add_virtual_intf = qtnf_add_virtual_intf, 76298f44cb0SIgor Mitsyanko .change_virtual_intf = qtnf_change_virtual_intf, 76398f44cb0SIgor Mitsyanko .del_virtual_intf = qtnf_del_virtual_intf, 76498f44cb0SIgor Mitsyanko .start_ap = qtnf_start_ap, 76598f44cb0SIgor Mitsyanko .change_beacon = qtnf_change_beacon, 76698f44cb0SIgor Mitsyanko .stop_ap = qtnf_stop_ap, 76798f44cb0SIgor Mitsyanko .set_wiphy_params = qtnf_set_wiphy_params, 76898f44cb0SIgor Mitsyanko .mgmt_frame_register = qtnf_mgmt_frame_register, 76998f44cb0SIgor Mitsyanko .mgmt_tx = qtnf_mgmt_tx, 77098f44cb0SIgor Mitsyanko .change_station = qtnf_change_station, 77198f44cb0SIgor Mitsyanko .del_station = qtnf_del_station, 77298f44cb0SIgor Mitsyanko .get_station = qtnf_get_station, 77398f44cb0SIgor Mitsyanko .dump_station = qtnf_dump_station, 77498f44cb0SIgor Mitsyanko .add_key = qtnf_add_key, 77598f44cb0SIgor Mitsyanko .del_key = qtnf_del_key, 77698f44cb0SIgor Mitsyanko .set_default_key = qtnf_set_default_key, 77798f44cb0SIgor Mitsyanko .set_default_mgmt_key = qtnf_set_default_mgmt_key, 77898f44cb0SIgor Mitsyanko .scan = qtnf_scan, 77998f44cb0SIgor Mitsyanko .connect = qtnf_connect, 7807c04b439SSergey Matyukevich .disconnect = qtnf_disconnect, 78127894448SSergey Matyukevich .dump_survey = qtnf_dump_survey, 78297883695SSergey Matyukevich .get_channel = qtnf_get_channel, 78397883695SSergey Matyukevich .channel_switch = qtnf_channel_switch 78498f44cb0SIgor Mitsyanko }; 78598f44cb0SIgor Mitsyanko 7864dd07d2bSSergey Matyukevich static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, 78798f44cb0SIgor Mitsyanko struct regulatory_request *req) 78898f44cb0SIgor Mitsyanko { 7894dd07d2bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy_in); 7904dd07d2bSSergey Matyukevich struct qtnf_bus *bus = mac->bus; 7914dd07d2bSSergey Matyukevich struct wiphy *wiphy; 7924dd07d2bSSergey Matyukevich unsigned int mac_idx; 79398f44cb0SIgor Mitsyanko enum nl80211_band band; 7944dd07d2bSSergey Matyukevich int ret; 79598f44cb0SIgor Mitsyanko 79698f44cb0SIgor Mitsyanko pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, 79798f44cb0SIgor Mitsyanko req->alpha2[0], req->alpha2[1]); 79898f44cb0SIgor Mitsyanko 7994dd07d2bSSergey Matyukevich ret = qtnf_cmd_reg_notify(bus, req); 8004dd07d2bSSergey Matyukevich if (ret) { 8014dd07d2bSSergey Matyukevich if (ret != -EOPNOTSUPP && ret != -EALREADY) 8024dd07d2bSSergey Matyukevich pr_err("failed to update reg domain to %c%c\n", 8034dd07d2bSSergey Matyukevich req->alpha2[0], req->alpha2[1]); 80498f44cb0SIgor Mitsyanko return; 80598f44cb0SIgor Mitsyanko } 80698f44cb0SIgor Mitsyanko 8074dd07d2bSSergey Matyukevich for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { 8084dd07d2bSSergey Matyukevich if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) 80998f44cb0SIgor Mitsyanko continue; 81098f44cb0SIgor Mitsyanko 8114dd07d2bSSergey Matyukevich mac = bus->mac[mac_idx]; 8124dd07d2bSSergey Matyukevich wiphy = priv_to_wiphy(mac); 81398f44cb0SIgor Mitsyanko 81498f44cb0SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; ++band) { 81598f44cb0SIgor Mitsyanko if (!wiphy->bands[band]) 81698f44cb0SIgor Mitsyanko continue; 81798f44cb0SIgor Mitsyanko 8184dd07d2bSSergey Matyukevich ret = qtnf_cmd_get_mac_chan_info(mac, 8194dd07d2bSSergey Matyukevich wiphy->bands[band]); 8204dd07d2bSSergey Matyukevich if (ret) 8214dd07d2bSSergey Matyukevich pr_err("failed to get chan info for mac %u band %u\n", 8224dd07d2bSSergey Matyukevich mac_idx, band); 82398f44cb0SIgor Mitsyanko } 82498f44cb0SIgor Mitsyanko } 82598f44cb0SIgor Mitsyanko } 82698f44cb0SIgor Mitsyanko 82798f44cb0SIgor Mitsyanko void qtnf_band_setup_htvht_caps(struct qtnf_mac_info *macinfo, 82898f44cb0SIgor Mitsyanko struct ieee80211_supported_band *band) 82998f44cb0SIgor Mitsyanko { 83098f44cb0SIgor Mitsyanko struct ieee80211_sta_ht_cap *ht_cap; 83198f44cb0SIgor Mitsyanko struct ieee80211_sta_vht_cap *vht_cap; 83298f44cb0SIgor Mitsyanko 83398f44cb0SIgor Mitsyanko ht_cap = &band->ht_cap; 83498f44cb0SIgor Mitsyanko ht_cap->ht_supported = true; 83598f44cb0SIgor Mitsyanko memcpy(&ht_cap->cap, &macinfo->ht_cap.cap_info, 83698f44cb0SIgor Mitsyanko sizeof(u16)); 83798f44cb0SIgor Mitsyanko ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; 83898f44cb0SIgor Mitsyanko ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; 83998f44cb0SIgor Mitsyanko memcpy(&ht_cap->mcs, &macinfo->ht_cap.mcs, 84098f44cb0SIgor Mitsyanko sizeof(ht_cap->mcs)); 84198f44cb0SIgor Mitsyanko 84298f44cb0SIgor Mitsyanko if (macinfo->phymode_cap & QLINK_PHYMODE_AC) { 84398f44cb0SIgor Mitsyanko vht_cap = &band->vht_cap; 84498f44cb0SIgor Mitsyanko vht_cap->vht_supported = true; 84598f44cb0SIgor Mitsyanko memcpy(&vht_cap->cap, 84698f44cb0SIgor Mitsyanko &macinfo->vht_cap.vht_cap_info, sizeof(u32)); 84798f44cb0SIgor Mitsyanko /* Update MCS support for VHT */ 84898f44cb0SIgor Mitsyanko memcpy(&vht_cap->vht_mcs, 84998f44cb0SIgor Mitsyanko &macinfo->vht_cap.supp_mcs, 85098f44cb0SIgor Mitsyanko sizeof(struct ieee80211_vht_mcs_info)); 85198f44cb0SIgor Mitsyanko } 85298f44cb0SIgor Mitsyanko } 85398f44cb0SIgor Mitsyanko 85498f44cb0SIgor Mitsyanko struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) 85598f44cb0SIgor Mitsyanko { 85698f44cb0SIgor Mitsyanko struct wiphy *wiphy; 85798f44cb0SIgor Mitsyanko 85898f44cb0SIgor Mitsyanko wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac)); 85998f44cb0SIgor Mitsyanko if (!wiphy) 86098f44cb0SIgor Mitsyanko return NULL; 86198f44cb0SIgor Mitsyanko 86298f44cb0SIgor Mitsyanko set_wiphy_dev(wiphy, bus->dev); 86398f44cb0SIgor Mitsyanko 86498f44cb0SIgor Mitsyanko return wiphy; 86598f44cb0SIgor Mitsyanko } 86698f44cb0SIgor Mitsyanko 86798f44cb0SIgor Mitsyanko static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, 86898f44cb0SIgor Mitsyanko struct ieee80211_iface_combination *if_comb, 86998f44cb0SIgor Mitsyanko const struct qtnf_mac_info *mac_info) 87098f44cb0SIgor Mitsyanko { 87198f44cb0SIgor Mitsyanko size_t max_interfaces = 0; 87298f44cb0SIgor Mitsyanko u16 interface_modes = 0; 87398f44cb0SIgor Mitsyanko size_t i; 87498f44cb0SIgor Mitsyanko 87598f44cb0SIgor Mitsyanko if (unlikely(!mac_info->limits || !mac_info->n_limits)) 87698f44cb0SIgor Mitsyanko return -ENOENT; 87798f44cb0SIgor Mitsyanko 87898f44cb0SIgor Mitsyanko if_comb->limits = mac_info->limits; 87998f44cb0SIgor Mitsyanko if_comb->n_limits = mac_info->n_limits; 88098f44cb0SIgor Mitsyanko 88198f44cb0SIgor Mitsyanko for (i = 0; i < mac_info->n_limits; i++) { 88298f44cb0SIgor Mitsyanko max_interfaces += mac_info->limits[i].max; 88398f44cb0SIgor Mitsyanko interface_modes |= mac_info->limits[i].types; 88498f44cb0SIgor Mitsyanko } 88598f44cb0SIgor Mitsyanko 88698f44cb0SIgor Mitsyanko if_comb->num_different_channels = 1; 88798f44cb0SIgor Mitsyanko if_comb->beacon_int_infra_match = true; 88898f44cb0SIgor Mitsyanko if_comb->max_interfaces = max_interfaces; 88998f44cb0SIgor Mitsyanko if_comb->radar_detect_widths = mac_info->radar_detect_widths; 89098f44cb0SIgor Mitsyanko wiphy->interface_modes = interface_modes; 89198f44cb0SIgor Mitsyanko 89298f44cb0SIgor Mitsyanko return 0; 89398f44cb0SIgor Mitsyanko } 89498f44cb0SIgor Mitsyanko 89598f44cb0SIgor Mitsyanko int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) 89698f44cb0SIgor Mitsyanko { 89798f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 89898f44cb0SIgor Mitsyanko struct ieee80211_iface_combination *iface_comb = NULL; 89998f44cb0SIgor Mitsyanko int ret; 90098f44cb0SIgor Mitsyanko 90198f44cb0SIgor Mitsyanko if (!wiphy) { 90298f44cb0SIgor Mitsyanko pr_err("invalid wiphy pointer\n"); 90398f44cb0SIgor Mitsyanko return -EFAULT; 90498f44cb0SIgor Mitsyanko } 90598f44cb0SIgor Mitsyanko 90698f44cb0SIgor Mitsyanko iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); 9074dd07d2bSSergey Matyukevich if (!iface_comb) 9084dd07d2bSSergey Matyukevich return -ENOMEM; 90998f44cb0SIgor Mitsyanko 91098f44cb0SIgor Mitsyanko ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); 91198f44cb0SIgor Mitsyanko if (ret) 91298f44cb0SIgor Mitsyanko goto out; 91398f44cb0SIgor Mitsyanko 91498f44cb0SIgor Mitsyanko pr_info("MAC%u: phymode=%#x radar=%#x\n", mac->macid, 91598f44cb0SIgor Mitsyanko mac->macinfo.phymode_cap, mac->macinfo.radar_detect_widths); 91698f44cb0SIgor Mitsyanko 91798f44cb0SIgor Mitsyanko wiphy->frag_threshold = mac->macinfo.frag_thr; 91898f44cb0SIgor Mitsyanko wiphy->rts_threshold = mac->macinfo.rts_thr; 91998f44cb0SIgor Mitsyanko wiphy->retry_short = mac->macinfo.sretry_limit; 92098f44cb0SIgor Mitsyanko wiphy->retry_long = mac->macinfo.lretry_limit; 92198f44cb0SIgor Mitsyanko wiphy->coverage_class = mac->macinfo.coverage_class; 92298f44cb0SIgor Mitsyanko 92398f44cb0SIgor Mitsyanko wiphy->max_scan_ssids = QTNF_MAX_SSID_LIST_LENGTH; 92498f44cb0SIgor Mitsyanko wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; 92598f44cb0SIgor Mitsyanko wiphy->mgmt_stypes = qtnf_mgmt_stypes; 92698f44cb0SIgor Mitsyanko wiphy->max_remain_on_channel_duration = 5000; 92798f44cb0SIgor Mitsyanko 92898f44cb0SIgor Mitsyanko wiphy->iface_combinations = iface_comb; 92998f44cb0SIgor Mitsyanko wiphy->n_iface_combinations = 1; 93097883695SSergey Matyukevich wiphy->max_num_csa_counters = 2; 93198f44cb0SIgor Mitsyanko 93298f44cb0SIgor Mitsyanko /* Initialize cipher suits */ 93398f44cb0SIgor Mitsyanko wiphy->cipher_suites = qtnf_cipher_suites; 93498f44cb0SIgor Mitsyanko wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); 93598f44cb0SIgor Mitsyanko wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 93698f44cb0SIgor Mitsyanko wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | 93798f44cb0SIgor Mitsyanko WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 93897883695SSergey Matyukevich WIPHY_FLAG_AP_UAPSD | 93997883695SSergey Matyukevich WIPHY_FLAG_HAS_CHANNEL_SWITCH; 94098f44cb0SIgor Mitsyanko 94198f44cb0SIgor Mitsyanko wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 94298f44cb0SIgor Mitsyanko NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; 94398f44cb0SIgor Mitsyanko 94498f44cb0SIgor Mitsyanko wiphy->available_antennas_tx = mac->macinfo.num_tx_chain; 94598f44cb0SIgor Mitsyanko wiphy->available_antennas_rx = mac->macinfo.num_rx_chain; 94698f44cb0SIgor Mitsyanko 94798f44cb0SIgor Mitsyanko wiphy->max_ap_assoc_sta = mac->macinfo.max_ap_assoc_sta; 94898f44cb0SIgor Mitsyanko 94998f44cb0SIgor Mitsyanko ether_addr_copy(wiphy->perm_addr, mac->macaddr); 95098f44cb0SIgor Mitsyanko 95198f44cb0SIgor Mitsyanko if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) { 9524dd07d2bSSergey Matyukevich wiphy->regulatory_flags |= REGULATORY_STRICT_REG | 9534dd07d2bSSergey Matyukevich REGULATORY_CUSTOM_REG; 95498f44cb0SIgor Mitsyanko wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; 9554dd07d2bSSergey Matyukevich wiphy_apply_custom_regulatory(wiphy, hw_info->rd); 95698f44cb0SIgor Mitsyanko } else { 95798f44cb0SIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; 95898f44cb0SIgor Mitsyanko } 95998f44cb0SIgor Mitsyanko 96098f44cb0SIgor Mitsyanko ret = wiphy_register(wiphy); 961ea19479fSSergey Matyukevich if (ret < 0) 962ea19479fSSergey Matyukevich goto out; 963ea19479fSSergey Matyukevich 964ea19479fSSergey Matyukevich if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) 965ea19479fSSergey Matyukevich ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); 966ea19479fSSergey Matyukevich else if (isalpha(hw_info->rd->alpha2[0]) && 967ea19479fSSergey Matyukevich isalpha(hw_info->rd->alpha2[1])) 968ea19479fSSergey Matyukevich ret = regulatory_hint(wiphy, hw_info->rd->alpha2); 969ea19479fSSergey Matyukevich 97098f44cb0SIgor Mitsyanko out: 9714dd07d2bSSergey Matyukevich if (ret) { 97298f44cb0SIgor Mitsyanko kfree(iface_comb); 97398f44cb0SIgor Mitsyanko return ret; 97498f44cb0SIgor Mitsyanko } 97598f44cb0SIgor Mitsyanko 97698f44cb0SIgor Mitsyanko return 0; 97798f44cb0SIgor Mitsyanko } 97898f44cb0SIgor Mitsyanko 97998f44cb0SIgor Mitsyanko void qtnf_netdev_updown(struct net_device *ndev, bool up) 98098f44cb0SIgor Mitsyanko { 98198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 98298f44cb0SIgor Mitsyanko 98398f44cb0SIgor Mitsyanko if (qtnf_cmd_send_updown_intf(vif, up)) 98498f44cb0SIgor Mitsyanko pr_err("failed to send up/down command to FW\n"); 98598f44cb0SIgor Mitsyanko } 98698f44cb0SIgor Mitsyanko 98798f44cb0SIgor Mitsyanko void qtnf_virtual_intf_cleanup(struct net_device *ndev) 98898f44cb0SIgor Mitsyanko { 98998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 9904f8e2545SColin Ian King struct qtnf_wmac *mac = wiphy_priv(vif->wdev.wiphy); 99198f44cb0SIgor Mitsyanko 99298f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 99398f44cb0SIgor Mitsyanko switch (vif->sta_state) { 99498f44cb0SIgor Mitsyanko case QTNF_STA_DISCONNECTED: 99598f44cb0SIgor Mitsyanko break; 99698f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTING: 99798f44cb0SIgor Mitsyanko cfg80211_connect_result(vif->netdev, 9989766d1ddSIgor Mitsyanko vif->bssid, NULL, 0, 99998f44cb0SIgor Mitsyanko NULL, 0, 100098f44cb0SIgor Mitsyanko WLAN_STATUS_UNSPECIFIED_FAILURE, 100198f44cb0SIgor Mitsyanko GFP_KERNEL); 100298f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 100398f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 100498f44cb0SIgor Mitsyanko break; 100598f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTED: 100698f44cb0SIgor Mitsyanko cfg80211_disconnected(vif->netdev, 100798f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING, 100898f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 100998f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 101098f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 101198f44cb0SIgor Mitsyanko break; 101298f44cb0SIgor Mitsyanko } 101398f44cb0SIgor Mitsyanko 101498f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 101598f44cb0SIgor Mitsyanko qtnf_scan_done(mac, true); 101698f44cb0SIgor Mitsyanko } 101798f44cb0SIgor Mitsyanko } 101898f44cb0SIgor Mitsyanko 101998f44cb0SIgor Mitsyanko void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) 102098f44cb0SIgor Mitsyanko { 102198f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 102298f44cb0SIgor Mitsyanko switch (vif->sta_state) { 102398f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTING: 102498f44cb0SIgor Mitsyanko cfg80211_connect_result(vif->netdev, 10259766d1ddSIgor Mitsyanko vif->bssid, NULL, 0, 102698f44cb0SIgor Mitsyanko NULL, 0, 102798f44cb0SIgor Mitsyanko WLAN_STATUS_UNSPECIFIED_FAILURE, 102898f44cb0SIgor Mitsyanko GFP_KERNEL); 102998f44cb0SIgor Mitsyanko break; 103098f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTED: 103198f44cb0SIgor Mitsyanko cfg80211_disconnected(vif->netdev, 103298f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING, 103398f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 103498f44cb0SIgor Mitsyanko break; 103598f44cb0SIgor Mitsyanko case QTNF_STA_DISCONNECTED: 103698f44cb0SIgor Mitsyanko break; 103798f44cb0SIgor Mitsyanko } 103898f44cb0SIgor Mitsyanko } 103998f44cb0SIgor Mitsyanko 104098f44cb0SIgor Mitsyanko cfg80211_shutdown_all_interfaces(vif->wdev.wiphy); 104198f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 104298f44cb0SIgor Mitsyanko } 104398f44cb0SIgor Mitsyanko 104498f44cb0SIgor Mitsyanko void qtnf_band_init_rates(struct ieee80211_supported_band *band) 104598f44cb0SIgor Mitsyanko { 104698f44cb0SIgor Mitsyanko switch (band->band) { 104798f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 104898f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_2g; 104998f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_2g); 105098f44cb0SIgor Mitsyanko break; 105198f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 105298f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_5g; 105398f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_5g); 105498f44cb0SIgor Mitsyanko break; 105598f44cb0SIgor Mitsyanko default: 105698f44cb0SIgor Mitsyanko band->bitrates = NULL; 105798f44cb0SIgor Mitsyanko band->n_bitrates = 0; 105898f44cb0SIgor Mitsyanko break; 105998f44cb0SIgor Mitsyanko } 106098f44cb0SIgor Mitsyanko } 1061