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) | 763dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | 773dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | 783dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | 793dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_AUTH >> 4), 8098f44cb0SIgor Mitsyanko }, 8198f44cb0SIgor Mitsyanko }; 8298f44cb0SIgor Mitsyanko 8398f44cb0SIgor Mitsyanko static int 8401efff52SSergey Matyukevich qtnf_validate_iface_combinations(struct wiphy *wiphy, 8501efff52SSergey Matyukevich struct qtnf_vif *change_vif, 8601efff52SSergey Matyukevich enum nl80211_iftype new_type) 8701efff52SSergey Matyukevich { 8801efff52SSergey Matyukevich struct qtnf_wmac *mac; 8901efff52SSergey Matyukevich struct qtnf_vif *vif; 9001efff52SSergey Matyukevich int i; 9101efff52SSergey Matyukevich int ret = 0; 9201efff52SSergey Matyukevich struct iface_combination_params params = { 9301efff52SSergey Matyukevich .num_different_channels = 1, 9401efff52SSergey Matyukevich }; 9501efff52SSergey Matyukevich 9601efff52SSergey Matyukevich mac = wiphy_priv(wiphy); 9701efff52SSergey Matyukevich if (!mac) 9801efff52SSergey Matyukevich return -EFAULT; 9901efff52SSergey Matyukevich 10001efff52SSergey Matyukevich for (i = 0; i < QTNF_MAX_INTF; i++) { 10101efff52SSergey Matyukevich vif = &mac->iflist[i]; 10201efff52SSergey Matyukevich if (vif->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) 10301efff52SSergey Matyukevich params.iftype_num[vif->wdev.iftype]++; 10401efff52SSergey Matyukevich } 10501efff52SSergey Matyukevich 10601efff52SSergey Matyukevich if (change_vif) { 10701efff52SSergey Matyukevich params.iftype_num[new_type]++; 10801efff52SSergey Matyukevich params.iftype_num[change_vif->wdev.iftype]--; 10901efff52SSergey Matyukevich } else { 11001efff52SSergey Matyukevich params.iftype_num[new_type]++; 11101efff52SSergey Matyukevich } 11201efff52SSergey Matyukevich 11301efff52SSergey Matyukevich ret = cfg80211_check_combinations(wiphy, ¶ms); 11401efff52SSergey Matyukevich 11501efff52SSergey Matyukevich return ret; 11601efff52SSergey Matyukevich } 11701efff52SSergey Matyukevich 11801efff52SSergey Matyukevich static int 11998f44cb0SIgor Mitsyanko qtnf_change_virtual_intf(struct wiphy *wiphy, 12098f44cb0SIgor Mitsyanko struct net_device *dev, 12198f44cb0SIgor Mitsyanko enum nl80211_iftype type, 12298f44cb0SIgor Mitsyanko struct vif_params *params) 12398f44cb0SIgor Mitsyanko { 12498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 12598f44cb0SIgor Mitsyanko u8 *mac_addr; 12698f44cb0SIgor Mitsyanko int ret; 12798f44cb0SIgor Mitsyanko 12801efff52SSergey Matyukevich ret = qtnf_validate_iface_combinations(wiphy, vif, type); 12901efff52SSergey Matyukevich if (ret) { 13001efff52SSergey Matyukevich pr_err("VIF%u.%u combination check: failed to set type %d\n", 13101efff52SSergey Matyukevich vif->mac->macid, vif->vifid, type); 13201efff52SSergey Matyukevich return ret; 13301efff52SSergey Matyukevich } 13401efff52SSergey Matyukevich 13598f44cb0SIgor Mitsyanko if (params) 13698f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 13798f44cb0SIgor Mitsyanko else 13898f44cb0SIgor Mitsyanko mac_addr = NULL; 13998f44cb0SIgor Mitsyanko 14098f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 14198f44cb0SIgor Mitsyanko 14298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_intf_type(vif, type, mac_addr); 14398f44cb0SIgor Mitsyanko if (ret) { 14498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change VIF type: %d\n", 14598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, ret); 14698f44cb0SIgor Mitsyanko return ret; 14798f44cb0SIgor Mitsyanko } 14898f44cb0SIgor Mitsyanko 14998f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 15098f44cb0SIgor Mitsyanko return 0; 15198f44cb0SIgor Mitsyanko } 15298f44cb0SIgor Mitsyanko 15398f44cb0SIgor Mitsyanko int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) 15498f44cb0SIgor Mitsyanko { 15598f44cb0SIgor Mitsyanko struct net_device *netdev = wdev->netdev; 15698f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 15798f44cb0SIgor Mitsyanko 15898f44cb0SIgor Mitsyanko if (WARN_ON(!netdev)) 15998f44cb0SIgor Mitsyanko return -EFAULT; 16098f44cb0SIgor Mitsyanko 16198f44cb0SIgor Mitsyanko vif = qtnf_netdev_get_priv(wdev->netdev); 16298f44cb0SIgor Mitsyanko 163a715b3a0SSergey Matyukevich qtnf_scan_done(vif->mac, true); 164a715b3a0SSergey Matyukevich 16598f44cb0SIgor Mitsyanko /* Stop data */ 16698f44cb0SIgor Mitsyanko netif_tx_stop_all_queues(netdev); 16798f44cb0SIgor Mitsyanko if (netif_carrier_ok(netdev)) 16898f44cb0SIgor Mitsyanko netif_carrier_off(netdev); 16998f44cb0SIgor Mitsyanko 17098f44cb0SIgor Mitsyanko if (netdev->reg_state == NETREG_REGISTERED) 17198f44cb0SIgor Mitsyanko unregister_netdevice(netdev); 17298f44cb0SIgor Mitsyanko 17387affddeSVasily Ulyanov if (qtnf_cmd_send_del_intf(vif)) 17487affddeSVasily Ulyanov pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, 17587affddeSVasily Ulyanov vif->vifid); 17687affddeSVasily Ulyanov 17798f44cb0SIgor Mitsyanko vif->netdev->ieee80211_ptr = NULL; 17898f44cb0SIgor Mitsyanko vif->netdev = NULL; 17998f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 18098f44cb0SIgor Mitsyanko 18198f44cb0SIgor Mitsyanko return 0; 18298f44cb0SIgor Mitsyanko } 18398f44cb0SIgor Mitsyanko 18498f44cb0SIgor Mitsyanko static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, 18598f44cb0SIgor Mitsyanko const char *name, 18698f44cb0SIgor Mitsyanko unsigned char name_assign_t, 18798f44cb0SIgor Mitsyanko enum nl80211_iftype type, 18898f44cb0SIgor Mitsyanko struct vif_params *params) 18998f44cb0SIgor Mitsyanko { 19098f44cb0SIgor Mitsyanko struct qtnf_wmac *mac; 19198f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 19298f44cb0SIgor Mitsyanko u8 *mac_addr = NULL; 19301efff52SSergey Matyukevich int ret; 19498f44cb0SIgor Mitsyanko 19598f44cb0SIgor Mitsyanko mac = wiphy_priv(wiphy); 19698f44cb0SIgor Mitsyanko 19798f44cb0SIgor Mitsyanko if (!mac) 19898f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 19998f44cb0SIgor Mitsyanko 20001efff52SSergey Matyukevich ret = qtnf_validate_iface_combinations(wiphy, NULL, type); 20101efff52SSergey Matyukevich if (ret) { 20201efff52SSergey Matyukevich pr_err("MAC%u invalid combination: failed to add type %d\n", 20301efff52SSergey Matyukevich mac->macid, type); 20401efff52SSergey Matyukevich return ERR_PTR(ret); 20501efff52SSergey Matyukevich } 20601efff52SSergey Matyukevich 20798f44cb0SIgor Mitsyanko switch (type) { 20898f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 20998f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 21098f44cb0SIgor Mitsyanko vif = qtnf_mac_get_free_vif(mac); 21198f44cb0SIgor Mitsyanko if (!vif) { 21298f44cb0SIgor Mitsyanko pr_err("MAC%u: no free VIF available\n", mac->macid); 21398f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 21498f44cb0SIgor Mitsyanko } 21598f44cb0SIgor Mitsyanko 21698f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 2179a3beeb5SSergey Matyukevich eth_zero_addr(vif->bssid); 21898f44cb0SIgor Mitsyanko vif->bss_priority = QTNF_DEF_BSS_PRIORITY; 2199a3beeb5SSergey Matyukevich memset(&vif->wdev, 0, sizeof(vif->wdev)); 22098f44cb0SIgor Mitsyanko vif->wdev.wiphy = wiphy; 22198f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 22298f44cb0SIgor Mitsyanko break; 22398f44cb0SIgor Mitsyanko default: 22498f44cb0SIgor Mitsyanko pr_err("MAC%u: unsupported IF type %d\n", mac->macid, type); 22598f44cb0SIgor Mitsyanko return ERR_PTR(-ENOTSUPP); 22698f44cb0SIgor Mitsyanko } 22798f44cb0SIgor Mitsyanko 22898f44cb0SIgor Mitsyanko if (params) 22998f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 23098f44cb0SIgor Mitsyanko 23198f44cb0SIgor Mitsyanko if (qtnf_cmd_send_add_intf(vif, type, mac_addr)) { 23298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add VIF\n", mac->macid, vif->vifid); 23398f44cb0SIgor Mitsyanko goto err_cmd; 23498f44cb0SIgor Mitsyanko } 23598f44cb0SIgor Mitsyanko 23698f44cb0SIgor Mitsyanko if (!is_valid_ether_addr(vif->mac_addr)) { 23798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: FW reported bad MAC: %pM\n", 23898f44cb0SIgor Mitsyanko mac->macid, vif->vifid, vif->mac_addr); 23998f44cb0SIgor Mitsyanko goto err_mac; 24098f44cb0SIgor Mitsyanko } 24198f44cb0SIgor Mitsyanko 242e6ef8cd0SIgor Mitsyanko if (qtnf_core_net_attach(mac, vif, name, name_assign_t)) { 24398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid, 24498f44cb0SIgor Mitsyanko vif->vifid); 24598f44cb0SIgor Mitsyanko goto err_net; 24698f44cb0SIgor Mitsyanko } 24798f44cb0SIgor Mitsyanko 24898f44cb0SIgor Mitsyanko vif->wdev.netdev = vif->netdev; 24998f44cb0SIgor Mitsyanko return &vif->wdev; 25098f44cb0SIgor Mitsyanko 25198f44cb0SIgor Mitsyanko err_net: 25298f44cb0SIgor Mitsyanko vif->netdev = NULL; 25398f44cb0SIgor Mitsyanko err_mac: 25498f44cb0SIgor Mitsyanko qtnf_cmd_send_del_intf(vif); 25598f44cb0SIgor Mitsyanko err_cmd: 25698f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 25798f44cb0SIgor Mitsyanko 25898f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 25998f44cb0SIgor Mitsyanko } 26098f44cb0SIgor Mitsyanko 26198f44cb0SIgor Mitsyanko static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, 26298f44cb0SIgor Mitsyanko const struct cfg80211_beacon_data *info) 26398f44cb0SIgor Mitsyanko { 26498f44cb0SIgor Mitsyanko int ret = 0; 26598f44cb0SIgor Mitsyanko 26698f44cb0SIgor Mitsyanko if (!info->beacon_ies || !info->beacon_ies_len) { 2674d1f0fabSIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, 26898f44cb0SIgor Mitsyanko NULL, 0); 26998f44cb0SIgor Mitsyanko } else { 2704d1f0fabSIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, 27198f44cb0SIgor Mitsyanko info->beacon_ies, 27298f44cb0SIgor Mitsyanko info->beacon_ies_len); 27398f44cb0SIgor Mitsyanko } 27498f44cb0SIgor Mitsyanko 27598f44cb0SIgor Mitsyanko if (ret) 27698f44cb0SIgor Mitsyanko goto out; 27798f44cb0SIgor Mitsyanko 27898f44cb0SIgor Mitsyanko if (!info->proberesp_ies || !info->proberesp_ies_len) { 27998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2804d1f0fabSIgor Mitsyanko QLINK_IE_SET_PROBE_RESP_IES, 28198f44cb0SIgor Mitsyanko NULL, 0); 28298f44cb0SIgor Mitsyanko } else { 28398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2844d1f0fabSIgor Mitsyanko QLINK_IE_SET_PROBE_RESP_IES, 28598f44cb0SIgor Mitsyanko info->proberesp_ies, 28698f44cb0SIgor Mitsyanko info->proberesp_ies_len); 28798f44cb0SIgor Mitsyanko } 28898f44cb0SIgor Mitsyanko 28998f44cb0SIgor Mitsyanko if (ret) 29098f44cb0SIgor Mitsyanko goto out; 29198f44cb0SIgor Mitsyanko 29298f44cb0SIgor Mitsyanko if (!info->assocresp_ies || !info->assocresp_ies_len) { 29398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2944d1f0fabSIgor Mitsyanko QLINK_IE_SET_ASSOC_RESP, 29598f44cb0SIgor Mitsyanko NULL, 0); 29698f44cb0SIgor Mitsyanko } else { 29798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2984d1f0fabSIgor Mitsyanko QLINK_IE_SET_ASSOC_RESP, 29998f44cb0SIgor Mitsyanko info->assocresp_ies, 30098f44cb0SIgor Mitsyanko info->assocresp_ies_len); 30198f44cb0SIgor Mitsyanko } 30298f44cb0SIgor Mitsyanko 30398f44cb0SIgor Mitsyanko out: 30498f44cb0SIgor Mitsyanko return ret; 30598f44cb0SIgor Mitsyanko } 30698f44cb0SIgor Mitsyanko 30798f44cb0SIgor Mitsyanko static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, 30898f44cb0SIgor Mitsyanko struct cfg80211_beacon_data *info) 30998f44cb0SIgor Mitsyanko { 31098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 31198f44cb0SIgor Mitsyanko 31298f44cb0SIgor Mitsyanko return qtnf_mgmt_set_appie(vif, info); 31398f44cb0SIgor Mitsyanko } 31498f44cb0SIgor Mitsyanko 31598f44cb0SIgor Mitsyanko static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, 31698f44cb0SIgor Mitsyanko struct cfg80211_ap_settings *settings) 31798f44cb0SIgor Mitsyanko { 31898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 31998f44cb0SIgor Mitsyanko int ret; 32098f44cb0SIgor Mitsyanko 32117011da0SIgor Mitsyanko ret = qtnf_cmd_send_start_ap(vif, settings); 322d7b80052SIgor Mitsyanko if (ret) 32398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, 32498f44cb0SIgor Mitsyanko vif->vifid); 32598f44cb0SIgor Mitsyanko 32698f44cb0SIgor Mitsyanko return ret; 32798f44cb0SIgor Mitsyanko } 32898f44cb0SIgor Mitsyanko 32998f44cb0SIgor Mitsyanko static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) 33098f44cb0SIgor Mitsyanko { 33198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 33298f44cb0SIgor Mitsyanko int ret; 33398f44cb0SIgor Mitsyanko 334a715b3a0SSergey Matyukevich qtnf_scan_done(vif->mac, true); 335a715b3a0SSergey Matyukevich 33698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_stop_ap(vif); 33798f44cb0SIgor Mitsyanko if (ret) { 33898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to stop AP operation in FW\n", 33998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 34098f44cb0SIgor Mitsyanko 34198f44cb0SIgor Mitsyanko netif_carrier_off(vif->netdev); 34298f44cb0SIgor Mitsyanko } 34398f44cb0SIgor Mitsyanko 34498f44cb0SIgor Mitsyanko return ret; 34598f44cb0SIgor Mitsyanko } 34698f44cb0SIgor Mitsyanko 34798f44cb0SIgor Mitsyanko static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed) 34898f44cb0SIgor Mitsyanko { 34998f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 35098f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 35198f44cb0SIgor Mitsyanko int ret; 35298f44cb0SIgor Mitsyanko 35398f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 35498f44cb0SIgor Mitsyanko if (!vif) { 35598f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 35698f44cb0SIgor Mitsyanko return -EFAULT; 35798f44cb0SIgor Mitsyanko } 35898f44cb0SIgor Mitsyanko 35998f44cb0SIgor Mitsyanko if (changed & (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) { 36098f44cb0SIgor Mitsyanko pr_err("MAC%u: can't modify retry params\n", mac->macid); 36198f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 36298f44cb0SIgor Mitsyanko } 36398f44cb0SIgor Mitsyanko 36498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_update_phy_params(mac, changed); 36598f44cb0SIgor Mitsyanko if (ret) 36698f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to update PHY params\n", mac->macid); 36798f44cb0SIgor Mitsyanko 36898f44cb0SIgor Mitsyanko return ret; 36998f44cb0SIgor Mitsyanko } 37098f44cb0SIgor Mitsyanko 37198f44cb0SIgor Mitsyanko static void 37298f44cb0SIgor Mitsyanko qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, 37398f44cb0SIgor Mitsyanko u16 frame_type, bool reg) 37498f44cb0SIgor Mitsyanko { 37598f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 37698f44cb0SIgor Mitsyanko u16 mgmt_type; 37798f44cb0SIgor Mitsyanko u16 new_mask; 37898f44cb0SIgor Mitsyanko u16 qlink_frame_type = 0; 37998f44cb0SIgor Mitsyanko 38098f44cb0SIgor Mitsyanko mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; 38198f44cb0SIgor Mitsyanko 38298f44cb0SIgor Mitsyanko if (reg) 38398f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask | BIT(mgmt_type); 38498f44cb0SIgor Mitsyanko else 38598f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask & ~BIT(mgmt_type); 38698f44cb0SIgor Mitsyanko 38798f44cb0SIgor Mitsyanko if (new_mask == vif->mgmt_frames_bitmask) 38898f44cb0SIgor Mitsyanko return; 38998f44cb0SIgor Mitsyanko 39098f44cb0SIgor Mitsyanko switch (frame_type & IEEE80211_FCTL_STYPE) { 3913dd06cecSSergey Matyukevich case IEEE80211_STYPE_REASSOC_REQ: 3923dd06cecSSergey Matyukevich case IEEE80211_STYPE_ASSOC_REQ: 3933dd06cecSSergey Matyukevich qlink_frame_type = QLINK_MGMT_FRAME_ASSOC_REQ; 3943dd06cecSSergey Matyukevich break; 3953dd06cecSSergey Matyukevich case IEEE80211_STYPE_AUTH: 3963dd06cecSSergey Matyukevich qlink_frame_type = QLINK_MGMT_FRAME_AUTH; 3973dd06cecSSergey Matyukevich break; 39898f44cb0SIgor Mitsyanko case IEEE80211_STYPE_PROBE_REQ: 39998f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ; 40098f44cb0SIgor Mitsyanko break; 40198f44cb0SIgor Mitsyanko case IEEE80211_STYPE_ACTION: 40298f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_ACTION; 40398f44cb0SIgor Mitsyanko break; 40498f44cb0SIgor Mitsyanko default: 40598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported frame type: %X\n", 40698f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, 40798f44cb0SIgor Mitsyanko (frame_type & IEEE80211_FCTL_STYPE) >> 4); 40898f44cb0SIgor Mitsyanko return; 40998f44cb0SIgor Mitsyanko } 41098f44cb0SIgor Mitsyanko 41198f44cb0SIgor Mitsyanko if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) { 41298f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: failed to %sregister mgmt frame type 0x%x\n", 41398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", 41498f44cb0SIgor Mitsyanko frame_type); 41598f44cb0SIgor Mitsyanko return; 41698f44cb0SIgor Mitsyanko } 41798f44cb0SIgor Mitsyanko 41898f44cb0SIgor Mitsyanko vif->mgmt_frames_bitmask = new_mask; 41998f44cb0SIgor Mitsyanko pr_debug("VIF%u.%u: %sregistered mgmt frame type 0x%x\n", 42098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", frame_type); 42198f44cb0SIgor Mitsyanko } 42298f44cb0SIgor Mitsyanko 42398f44cb0SIgor Mitsyanko static int 42498f44cb0SIgor Mitsyanko qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 42598f44cb0SIgor Mitsyanko struct cfg80211_mgmt_tx_params *params, u64 *cookie) 42698f44cb0SIgor Mitsyanko { 42798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 42898f44cb0SIgor Mitsyanko const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; 42998f44cb0SIgor Mitsyanko u32 short_cookie = prandom_u32(); 43098f44cb0SIgor Mitsyanko u16 flags = 0; 431e6e594afSIgor Mitsyanko u16 freq; 43298f44cb0SIgor Mitsyanko 43398f44cb0SIgor Mitsyanko *cookie = short_cookie; 43498f44cb0SIgor Mitsyanko 43598f44cb0SIgor Mitsyanko if (params->offchan) 43698f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN; 43798f44cb0SIgor Mitsyanko 43898f44cb0SIgor Mitsyanko if (params->no_cck) 43998f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_NO_CCK; 44098f44cb0SIgor Mitsyanko 44198f44cb0SIgor Mitsyanko if (params->dont_wait_for_ack) 44298f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT; 44398f44cb0SIgor Mitsyanko 444e6e594afSIgor Mitsyanko /* If channel is not specified, pass "freq = 0" to tell device 445e6e594afSIgor Mitsyanko * firmware to use current channel. 446e6e594afSIgor Mitsyanko */ 447e6e594afSIgor Mitsyanko if (params->chan) 448e6e594afSIgor Mitsyanko freq = params->chan->center_freq; 449e6e594afSIgor Mitsyanko else 450e6e594afSIgor Mitsyanko freq = 0; 451e6e594afSIgor Mitsyanko 45298f44cb0SIgor Mitsyanko pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n", 453e6e594afSIgor Mitsyanko wdev->netdev->name, freq, 45498f44cb0SIgor Mitsyanko le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da, 45598f44cb0SIgor Mitsyanko params->len, short_cookie, flags); 45698f44cb0SIgor Mitsyanko 45798f44cb0SIgor Mitsyanko return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags, 458e6e594afSIgor Mitsyanko freq, 45998f44cb0SIgor Mitsyanko params->buf, params->len); 46098f44cb0SIgor Mitsyanko } 46198f44cb0SIgor Mitsyanko 46298f44cb0SIgor Mitsyanko static int 46398f44cb0SIgor Mitsyanko qtnf_get_station(struct wiphy *wiphy, struct net_device *dev, 46498f44cb0SIgor Mitsyanko const u8 *mac, struct station_info *sinfo) 46598f44cb0SIgor Mitsyanko { 46698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 46798f44cb0SIgor Mitsyanko 4687a4d3a3bSIgor Mitsyanko sinfo->generation = vif->generation; 46998f44cb0SIgor Mitsyanko return qtnf_cmd_get_sta_info(vif, mac, sinfo); 47098f44cb0SIgor Mitsyanko } 47198f44cb0SIgor Mitsyanko 47298f44cb0SIgor Mitsyanko static int 47398f44cb0SIgor Mitsyanko qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev, 47498f44cb0SIgor Mitsyanko int idx, u8 *mac, struct station_info *sinfo) 47598f44cb0SIgor Mitsyanko { 47698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 47798f44cb0SIgor Mitsyanko const struct qtnf_sta_node *sta_node; 47898f44cb0SIgor Mitsyanko int ret; 47998f44cb0SIgor Mitsyanko 48098f44cb0SIgor Mitsyanko sta_node = qtnf_sta_list_lookup_index(&vif->sta_list, idx); 48198f44cb0SIgor Mitsyanko 48298f44cb0SIgor Mitsyanko if (unlikely(!sta_node)) 48398f44cb0SIgor Mitsyanko return -ENOENT; 48498f44cb0SIgor Mitsyanko 48598f44cb0SIgor Mitsyanko ether_addr_copy(mac, sta_node->mac_addr); 48698f44cb0SIgor Mitsyanko 48798f44cb0SIgor Mitsyanko ret = qtnf_cmd_get_sta_info(vif, sta_node->mac_addr, sinfo); 48898f44cb0SIgor Mitsyanko 48998f44cb0SIgor Mitsyanko if (unlikely(ret == -ENOENT)) { 4907a4d3a3bSIgor Mitsyanko qtnf_sta_list_del(vif, mac); 49198f44cb0SIgor Mitsyanko cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL); 49298f44cb0SIgor Mitsyanko sinfo->filled = 0; 49398f44cb0SIgor Mitsyanko } 49498f44cb0SIgor Mitsyanko 4957a4d3a3bSIgor Mitsyanko sinfo->generation = vif->generation; 4967a4d3a3bSIgor Mitsyanko 49798f44cb0SIgor Mitsyanko return ret; 49898f44cb0SIgor Mitsyanko } 49998f44cb0SIgor Mitsyanko 50098f44cb0SIgor Mitsyanko static int qtnf_add_key(struct wiphy *wiphy, struct net_device *dev, 50198f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr, 50298f44cb0SIgor Mitsyanko struct key_params *params) 50398f44cb0SIgor Mitsyanko { 50498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 50598f44cb0SIgor Mitsyanko int ret; 50698f44cb0SIgor Mitsyanko 50798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params); 50898f44cb0SIgor Mitsyanko if (ret) 50998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add key: cipher=%x idx=%u pw=%u\n", 51098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->cipher, key_index, 51198f44cb0SIgor Mitsyanko pairwise); 51298f44cb0SIgor Mitsyanko 51398f44cb0SIgor Mitsyanko return ret; 51498f44cb0SIgor Mitsyanko } 51598f44cb0SIgor Mitsyanko 51698f44cb0SIgor Mitsyanko static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev, 51798f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr) 51898f44cb0SIgor Mitsyanko { 51998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 52098f44cb0SIgor Mitsyanko int ret; 52198f44cb0SIgor Mitsyanko 52298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr); 52398f44cb0SIgor Mitsyanko if (ret) 52498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n", 52598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, pairwise); 52698f44cb0SIgor Mitsyanko 52798f44cb0SIgor Mitsyanko return ret; 52898f44cb0SIgor Mitsyanko } 52998f44cb0SIgor Mitsyanko 53098f44cb0SIgor Mitsyanko static int qtnf_set_default_key(struct wiphy *wiphy, struct net_device *dev, 53198f44cb0SIgor Mitsyanko u8 key_index, bool unicast, bool multicast) 53298f44cb0SIgor Mitsyanko { 53398f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 53498f44cb0SIgor Mitsyanko int ret; 53598f44cb0SIgor Mitsyanko 53698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_key(vif, key_index, unicast, multicast); 53798f44cb0SIgor Mitsyanko if (ret) 53898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set dflt key: idx=%u uc=%u mc=%u\n", 53998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, unicast, 54098f44cb0SIgor Mitsyanko multicast); 54198f44cb0SIgor Mitsyanko 54298f44cb0SIgor Mitsyanko return ret; 54398f44cb0SIgor Mitsyanko } 54498f44cb0SIgor Mitsyanko 54598f44cb0SIgor Mitsyanko static int 54698f44cb0SIgor Mitsyanko qtnf_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, 54798f44cb0SIgor Mitsyanko u8 key_index) 54898f44cb0SIgor Mitsyanko { 54998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 55098f44cb0SIgor Mitsyanko int ret; 55198f44cb0SIgor Mitsyanko 55298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_mgmt_key(vif, key_index); 55398f44cb0SIgor Mitsyanko if (ret) 55498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set default MGMT key: idx=%u\n", 55598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index); 55698f44cb0SIgor Mitsyanko 55798f44cb0SIgor Mitsyanko return ret; 55898f44cb0SIgor Mitsyanko } 55998f44cb0SIgor Mitsyanko 56098f44cb0SIgor Mitsyanko static int 56198f44cb0SIgor Mitsyanko qtnf_change_station(struct wiphy *wiphy, struct net_device *dev, 56298f44cb0SIgor Mitsyanko const u8 *mac, struct station_parameters *params) 56398f44cb0SIgor Mitsyanko { 56498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 56598f44cb0SIgor Mitsyanko int ret; 56698f44cb0SIgor Mitsyanko 56798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_sta(vif, mac, params); 56898f44cb0SIgor Mitsyanko if (ret) 56998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change STA %pM\n", 57098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, mac); 57198f44cb0SIgor Mitsyanko 57298f44cb0SIgor Mitsyanko return ret; 57398f44cb0SIgor Mitsyanko } 57498f44cb0SIgor Mitsyanko 57598f44cb0SIgor Mitsyanko static int 57698f44cb0SIgor Mitsyanko qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, 57798f44cb0SIgor Mitsyanko struct station_del_parameters *params) 57898f44cb0SIgor Mitsyanko { 57998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 58098f44cb0SIgor Mitsyanko int ret; 58198f44cb0SIgor Mitsyanko 58298f44cb0SIgor Mitsyanko if (params->mac && 58398f44cb0SIgor Mitsyanko (vif->wdev.iftype == NL80211_IFTYPE_AP) && 58498f44cb0SIgor Mitsyanko !is_broadcast_ether_addr(params->mac) && 58598f44cb0SIgor Mitsyanko !qtnf_sta_list_lookup(&vif->sta_list, params->mac)) 58698f44cb0SIgor Mitsyanko return 0; 58798f44cb0SIgor Mitsyanko 58898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_sta(vif, params); 58998f44cb0SIgor Mitsyanko if (ret) 59098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete STA %pM\n", 59198f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->mac); 59298f44cb0SIgor Mitsyanko return ret; 59398f44cb0SIgor Mitsyanko } 59498f44cb0SIgor Mitsyanko 59598f44cb0SIgor Mitsyanko static int 59698f44cb0SIgor Mitsyanko qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) 59798f44cb0SIgor Mitsyanko { 59898f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 59998f44cb0SIgor Mitsyanko 600f2cddd54SIgor Mitsyanko cancel_delayed_work_sync(&mac->scan_timeout); 601f2cddd54SIgor Mitsyanko 60298f44cb0SIgor Mitsyanko mac->scan_req = request; 60398f44cb0SIgor Mitsyanko 604c7ead2abSSergey Matyukevich if (qtnf_cmd_send_scan(mac)) { 60598f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to start scan\n", mac->macid); 606c7ead2abSSergey Matyukevich mac->scan_req = NULL; 607c7ead2abSSergey Matyukevich return -EFAULT; 608c7ead2abSSergey Matyukevich } 60998f44cb0SIgor Mitsyanko 610f2cddd54SIgor Mitsyanko queue_delayed_work(mac->bus->workqueue, &mac->scan_timeout, 611f2cddd54SIgor Mitsyanko QTNF_SCAN_TIMEOUT_SEC * HZ); 612c7ead2abSSergey Matyukevich 613c7ead2abSSergey Matyukevich return 0; 61498f44cb0SIgor Mitsyanko } 61598f44cb0SIgor Mitsyanko 61698f44cb0SIgor Mitsyanko static int 61798f44cb0SIgor Mitsyanko qtnf_connect(struct wiphy *wiphy, struct net_device *dev, 61898f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 61998f44cb0SIgor Mitsyanko { 62098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 62198f44cb0SIgor Mitsyanko int ret; 62298f44cb0SIgor Mitsyanko 62398f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 62498f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 62598f44cb0SIgor Mitsyanko 62698f44cb0SIgor Mitsyanko if (sme->bssid) 6279766d1ddSIgor Mitsyanko ether_addr_copy(vif->bssid, sme->bssid); 62898f44cb0SIgor Mitsyanko else 6299766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 63098f44cb0SIgor Mitsyanko 63198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_connect(vif, sme); 63298f44cb0SIgor Mitsyanko if (ret) { 63398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to connect\n", vif->mac->macid, 63498f44cb0SIgor Mitsyanko vif->vifid); 63598f44cb0SIgor Mitsyanko return ret; 63698f44cb0SIgor Mitsyanko } 63798f44cb0SIgor Mitsyanko 63898f44cb0SIgor Mitsyanko return 0; 63998f44cb0SIgor Mitsyanko } 64098f44cb0SIgor Mitsyanko 64198f44cb0SIgor Mitsyanko static int 64298f44cb0SIgor Mitsyanko qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, 64398f44cb0SIgor Mitsyanko u16 reason_code) 64498f44cb0SIgor Mitsyanko { 64598f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 64698f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 647480daa9cSSergey Matyukevich int ret = 0; 64898f44cb0SIgor Mitsyanko 64998f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 65098f44cb0SIgor Mitsyanko if (!vif) { 65198f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 652c1e3f64fSGustavo A. R. Silva return -EFAULT; 65398f44cb0SIgor Mitsyanko } 65498f44cb0SIgor Mitsyanko 655480daa9cSSergey Matyukevich if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { 656480daa9cSSergey Matyukevich ret = -EOPNOTSUPP; 657480daa9cSSergey Matyukevich goto out; 658480daa9cSSergey Matyukevich } 65998f44cb0SIgor Mitsyanko 66040d68dbbSAndrey Shevchenko qtnf_scan_done(mac, true); 66140d68dbbSAndrey Shevchenko 66298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_disconnect(vif, reason_code); 66398f44cb0SIgor Mitsyanko if (ret) { 66498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to disconnect\n", mac->macid, 66598f44cb0SIgor Mitsyanko vif->vifid); 666480daa9cSSergey Matyukevich goto out; 66798f44cb0SIgor Mitsyanko } 66898f44cb0SIgor Mitsyanko 669480daa9cSSergey Matyukevich out: 670480daa9cSSergey Matyukevich return ret; 67198f44cb0SIgor Mitsyanko } 67298f44cb0SIgor Mitsyanko 6737c04b439SSergey Matyukevich static int 6747c04b439SSergey Matyukevich qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, 6757c04b439SSergey Matyukevich int idx, struct survey_info *survey) 6767c04b439SSergey Matyukevich { 6777c04b439SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 678ef81e8e9SIgor Mitsyanko struct wireless_dev *wdev = dev->ieee80211_ptr; 6797c04b439SSergey Matyukevich struct ieee80211_supported_band *sband; 680ef81e8e9SIgor Mitsyanko const struct cfg80211_chan_def *chandef = &wdev->chandef; 6817c04b439SSergey Matyukevich struct ieee80211_channel *chan; 6827c04b439SSergey Matyukevich struct qtnf_chan_stats stats; 6837c04b439SSergey Matyukevich int ret; 6847c04b439SSergey Matyukevich 6857c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_2GHZ]; 6867c04b439SSergey Matyukevich if (sband && idx >= sband->n_channels) { 6877c04b439SSergey Matyukevich idx -= sband->n_channels; 6887c04b439SSergey Matyukevich sband = NULL; 6897c04b439SSergey Matyukevich } 6907c04b439SSergey Matyukevich 6917c04b439SSergey Matyukevich if (!sband) 6927c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_5GHZ]; 6937c04b439SSergey Matyukevich 6947c04b439SSergey Matyukevich if (!sband || idx >= sband->n_channels) 6957c04b439SSergey Matyukevich return -ENOENT; 6967c04b439SSergey Matyukevich 6977c04b439SSergey Matyukevich chan = &sband->channels[idx]; 6987c04b439SSergey Matyukevich memset(&stats, 0, sizeof(stats)); 6997c04b439SSergey Matyukevich 7007c04b439SSergey Matyukevich survey->channel = chan; 7017c04b439SSergey Matyukevich survey->filled = 0x0; 7027c04b439SSergey Matyukevich 70334f1145bSSergey Matyukevich if (chandef->chan) { 70434f1145bSSergey Matyukevich if (chan->hw_value == chandef->chan->hw_value) 70534f1145bSSergey Matyukevich survey->filled = SURVEY_INFO_IN_USE; 70634f1145bSSergey Matyukevich } 70727894448SSergey Matyukevich 7087c04b439SSergey Matyukevich ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); 7097c04b439SSergey Matyukevich switch (ret) { 7107c04b439SSergey Matyukevich case 0: 7117c04b439SSergey Matyukevich if (unlikely(stats.chan_num != chan->hw_value)) { 7127c04b439SSergey Matyukevich pr_err("received stats for channel %d instead of %d\n", 7137c04b439SSergey Matyukevich stats.chan_num, chan->hw_value); 7147c04b439SSergey Matyukevich ret = -EINVAL; 7157c04b439SSergey Matyukevich break; 7167c04b439SSergey Matyukevich } 7177c04b439SSergey Matyukevich 71834f1145bSSergey Matyukevich survey->filled |= SURVEY_INFO_TIME | 7197c04b439SSergey Matyukevich SURVEY_INFO_TIME_SCAN | 7207c04b439SSergey Matyukevich SURVEY_INFO_TIME_BUSY | 7217c04b439SSergey Matyukevich SURVEY_INFO_TIME_RX | 7227c04b439SSergey Matyukevich SURVEY_INFO_TIME_TX | 7237c04b439SSergey Matyukevich SURVEY_INFO_NOISE_DBM; 7247c04b439SSergey Matyukevich 7257c04b439SSergey Matyukevich survey->time_scan = stats.cca_try; 7267c04b439SSergey Matyukevich survey->time = stats.cca_try; 7277c04b439SSergey Matyukevich survey->time_tx = stats.cca_tx; 7287c04b439SSergey Matyukevich survey->time_rx = stats.cca_rx; 7297c04b439SSergey Matyukevich survey->time_busy = stats.cca_busy; 7307c04b439SSergey Matyukevich survey->noise = stats.chan_noise; 7317c04b439SSergey Matyukevich break; 7327c04b439SSergey Matyukevich case -ENOENT: 7337c04b439SSergey Matyukevich pr_debug("no stats for channel %u\n", chan->hw_value); 7347c04b439SSergey Matyukevich ret = 0; 7357c04b439SSergey Matyukevich break; 7367c04b439SSergey Matyukevich default: 7377c04b439SSergey Matyukevich pr_debug("failed to get chan(%d) stats from card\n", 7387c04b439SSergey Matyukevich chan->hw_value); 7397c04b439SSergey Matyukevich ret = -EINVAL; 7407c04b439SSergey Matyukevich break; 7417c04b439SSergey Matyukevich } 7427c04b439SSergey Matyukevich 7437c04b439SSergey Matyukevich return ret; 7447c04b439SSergey Matyukevich } 7457c04b439SSergey Matyukevich 74627894448SSergey Matyukevich static int 74727894448SSergey Matyukevich qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, 74827894448SSergey Matyukevich struct cfg80211_chan_def *chandef) 74927894448SSergey Matyukevich { 75027894448SSergey Matyukevich struct net_device *ndev = wdev->netdev; 75127894448SSergey Matyukevich struct qtnf_vif *vif; 7529e5478b6SIgor Mitsyanko int ret; 75327894448SSergey Matyukevich 75427894448SSergey Matyukevich if (!ndev) 75527894448SSergey Matyukevich return -ENODEV; 75627894448SSergey Matyukevich 75727894448SSergey Matyukevich vif = qtnf_netdev_get_priv(wdev->netdev); 75827894448SSergey Matyukevich 7599e5478b6SIgor Mitsyanko ret = qtnf_cmd_get_channel(vif, chandef); 7609e5478b6SIgor Mitsyanko if (ret) { 7619e5478b6SIgor Mitsyanko pr_err("%s: failed to get channel: %d\n", ndev->name, ret); 7629e5478b6SIgor Mitsyanko goto out; 76327894448SSergey Matyukevich } 76427894448SSergey Matyukevich 7659e5478b6SIgor Mitsyanko if (!cfg80211_chandef_valid(chandef)) { 7665bf374abSSergey Matyukevich pr_err("%s: bad channel freq=%u cf1=%u cf2=%u bw=%u\n", 7675bf374abSSergey Matyukevich ndev->name, chandef->chan->center_freq, 7689e5478b6SIgor Mitsyanko chandef->center_freq1, chandef->center_freq2, 7699e5478b6SIgor Mitsyanko chandef->width); 7709e5478b6SIgor Mitsyanko ret = -ENODATA; 77134f1145bSSergey Matyukevich } 77234f1145bSSergey Matyukevich 7739e5478b6SIgor Mitsyanko out: 7749e5478b6SIgor Mitsyanko return ret; 77527894448SSergey Matyukevich } 77627894448SSergey Matyukevich 77797883695SSergey Matyukevich static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, 77897883695SSergey Matyukevich struct cfg80211_csa_settings *params) 77997883695SSergey Matyukevich { 78097883695SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 78197883695SSergey Matyukevich int ret; 78297883695SSergey Matyukevich 78397883695SSergey Matyukevich pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, 78497883695SSergey Matyukevich params->chandef.chan->hw_value, params->count, 78597883695SSergey Matyukevich params->radar_required, params->block_tx); 78697883695SSergey Matyukevich 78797883695SSergey Matyukevich if (!cfg80211_chandef_valid(¶ms->chandef)) { 78897883695SSergey Matyukevich pr_err("%s: invalid channel\n", dev->name); 78997883695SSergey Matyukevich return -EINVAL; 79097883695SSergey Matyukevich } 79197883695SSergey Matyukevich 7928c015b90SIgor Mitsyanko ret = qtnf_cmd_send_chan_switch(vif, params); 79397883695SSergey Matyukevich if (ret) 79497883695SSergey Matyukevich pr_warn("%s: failed to switch to channel (%u)\n", 79597883695SSergey Matyukevich dev->name, params->chandef.chan->hw_value); 79697883695SSergey Matyukevich 79797883695SSergey Matyukevich return ret; 79897883695SSergey Matyukevich } 79997883695SSergey Matyukevich 800b05ee456SIgor Mitsyanko static int qtnf_start_radar_detection(struct wiphy *wiphy, 801b05ee456SIgor Mitsyanko struct net_device *ndev, 802b05ee456SIgor Mitsyanko struct cfg80211_chan_def *chandef, 803b05ee456SIgor Mitsyanko u32 cac_time_ms) 804b05ee456SIgor Mitsyanko { 805b05ee456SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 806b05ee456SIgor Mitsyanko int ret; 807b05ee456SIgor Mitsyanko 808fbb93020SDmitry Lebed if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) 809fbb93020SDmitry Lebed return -ENOTSUPP; 810fbb93020SDmitry Lebed 811b05ee456SIgor Mitsyanko ret = qtnf_cmd_start_cac(vif, chandef, cac_time_ms); 812b05ee456SIgor Mitsyanko if (ret) 813b05ee456SIgor Mitsyanko pr_err("%s: failed to start CAC ret=%d\n", ndev->name, ret); 814b05ee456SIgor Mitsyanko 815b05ee456SIgor Mitsyanko return ret; 816b05ee456SIgor Mitsyanko } 817b05ee456SIgor Mitsyanko 818f1398fd2SVasily Ulyanov static int qtnf_set_mac_acl(struct wiphy *wiphy, 819f1398fd2SVasily Ulyanov struct net_device *dev, 820f1398fd2SVasily Ulyanov const struct cfg80211_acl_data *params) 821f1398fd2SVasily Ulyanov { 822f1398fd2SVasily Ulyanov struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 823f1398fd2SVasily Ulyanov int ret; 824f1398fd2SVasily Ulyanov 825f1398fd2SVasily Ulyanov ret = qtnf_cmd_set_mac_acl(vif, params); 826f1398fd2SVasily Ulyanov if (ret) 827f1398fd2SVasily Ulyanov pr_err("%s: failed to set mac ACL ret=%d\n", dev->name, ret); 828f1398fd2SVasily Ulyanov 829f1398fd2SVasily Ulyanov return ret; 830f1398fd2SVasily Ulyanov } 831f1398fd2SVasily Ulyanov 8324775ad06SSergei Maksimenko static int qtnf_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, 8334775ad06SSergei Maksimenko bool enabled, int timeout) 8344775ad06SSergei Maksimenko { 8354775ad06SSergei Maksimenko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 8364775ad06SSergei Maksimenko int ret; 8374775ad06SSergei Maksimenko 8384775ad06SSergei Maksimenko ret = qtnf_cmd_send_pm_set(vif, enabled ? QLINK_PM_AUTO_STANDBY : 8394775ad06SSergei Maksimenko QLINK_PM_OFF, timeout); 8404775ad06SSergei Maksimenko if (ret) { 8414775ad06SSergei Maksimenko pr_err("%s: failed to set PM mode ret=%d\n", dev->name, ret); 8424775ad06SSergei Maksimenko return ret; 8434775ad06SSergei Maksimenko } 8444775ad06SSergei Maksimenko 8454775ad06SSergei Maksimenko return ret; 8464775ad06SSergei Maksimenko } 8474775ad06SSergei Maksimenko 84828b91884SSergey Matyukevich #ifdef CONFIG_PM 84928b91884SSergey Matyukevich static int qtnf_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) 85028b91884SSergey Matyukevich { 85128b91884SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 85228b91884SSergey Matyukevich struct qtnf_vif *vif; 85328b91884SSergey Matyukevich int ret = 0; 85428b91884SSergey Matyukevich 85528b91884SSergey Matyukevich vif = qtnf_mac_get_base_vif(mac); 85628b91884SSergey Matyukevich if (!vif) { 85728b91884SSergey Matyukevich pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 85828b91884SSergey Matyukevich ret = -EFAULT; 85928b91884SSergey Matyukevich goto exit; 86028b91884SSergey Matyukevich } 86128b91884SSergey Matyukevich 86228b91884SSergey Matyukevich if (!wowlan) { 86328b91884SSergey Matyukevich pr_debug("WoWLAN triggers are not enabled\n"); 86428b91884SSergey Matyukevich qtnf_virtual_intf_cleanup(vif->netdev); 86528b91884SSergey Matyukevich goto exit; 86628b91884SSergey Matyukevich } 86728b91884SSergey Matyukevich 86828b91884SSergey Matyukevich qtnf_scan_done(vif->mac, true); 86928b91884SSergey Matyukevich 87028b91884SSergey Matyukevich ret = qtnf_cmd_send_wowlan_set(vif, wowlan); 87128b91884SSergey Matyukevich if (ret) { 87228b91884SSergey Matyukevich pr_err("MAC%u: failed to set WoWLAN triggers\n", 87328b91884SSergey Matyukevich mac->macid); 87428b91884SSergey Matyukevich goto exit; 87528b91884SSergey Matyukevich } 87628b91884SSergey Matyukevich 87728b91884SSergey Matyukevich exit: 87828b91884SSergey Matyukevich return ret; 87928b91884SSergey Matyukevich } 88028b91884SSergey Matyukevich 88128b91884SSergey Matyukevich static int qtnf_resume(struct wiphy *wiphy) 88228b91884SSergey Matyukevich { 88328b91884SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 88428b91884SSergey Matyukevich struct qtnf_vif *vif; 88528b91884SSergey Matyukevich int ret = 0; 88628b91884SSergey Matyukevich 88728b91884SSergey Matyukevich vif = qtnf_mac_get_base_vif(mac); 88828b91884SSergey Matyukevich if (!vif) { 88928b91884SSergey Matyukevich pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 89028b91884SSergey Matyukevich ret = -EFAULT; 89128b91884SSergey Matyukevich goto exit; 89228b91884SSergey Matyukevich } 89328b91884SSergey Matyukevich 89428b91884SSergey Matyukevich ret = qtnf_cmd_send_wowlan_set(vif, NULL); 89528b91884SSergey Matyukevich if (ret) { 89628b91884SSergey Matyukevich pr_err("MAC%u: failed to reset WoWLAN triggers\n", 89728b91884SSergey Matyukevich mac->macid); 89828b91884SSergey Matyukevich goto exit; 89928b91884SSergey Matyukevich } 90028b91884SSergey Matyukevich 90128b91884SSergey Matyukevich exit: 90228b91884SSergey Matyukevich return ret; 90328b91884SSergey Matyukevich } 90428b91884SSergey Matyukevich 90528b91884SSergey Matyukevich static void qtnf_set_wakeup(struct wiphy *wiphy, bool enabled) 90628b91884SSergey Matyukevich { 90728b91884SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 90828b91884SSergey Matyukevich struct qtnf_bus *bus = mac->bus; 90928b91884SSergey Matyukevich 91028b91884SSergey Matyukevich device_set_wakeup_enable(bus->dev, enabled); 91128b91884SSergey Matyukevich } 91228b91884SSergey Matyukevich #endif 91328b91884SSergey Matyukevich 91498f44cb0SIgor Mitsyanko static struct cfg80211_ops qtn_cfg80211_ops = { 91598f44cb0SIgor Mitsyanko .add_virtual_intf = qtnf_add_virtual_intf, 91698f44cb0SIgor Mitsyanko .change_virtual_intf = qtnf_change_virtual_intf, 91798f44cb0SIgor Mitsyanko .del_virtual_intf = qtnf_del_virtual_intf, 91898f44cb0SIgor Mitsyanko .start_ap = qtnf_start_ap, 91998f44cb0SIgor Mitsyanko .change_beacon = qtnf_change_beacon, 92098f44cb0SIgor Mitsyanko .stop_ap = qtnf_stop_ap, 92198f44cb0SIgor Mitsyanko .set_wiphy_params = qtnf_set_wiphy_params, 92298f44cb0SIgor Mitsyanko .mgmt_frame_register = qtnf_mgmt_frame_register, 92398f44cb0SIgor Mitsyanko .mgmt_tx = qtnf_mgmt_tx, 92498f44cb0SIgor Mitsyanko .change_station = qtnf_change_station, 92598f44cb0SIgor Mitsyanko .del_station = qtnf_del_station, 92698f44cb0SIgor Mitsyanko .get_station = qtnf_get_station, 92798f44cb0SIgor Mitsyanko .dump_station = qtnf_dump_station, 92898f44cb0SIgor Mitsyanko .add_key = qtnf_add_key, 92998f44cb0SIgor Mitsyanko .del_key = qtnf_del_key, 93098f44cb0SIgor Mitsyanko .set_default_key = qtnf_set_default_key, 93198f44cb0SIgor Mitsyanko .set_default_mgmt_key = qtnf_set_default_mgmt_key, 93298f44cb0SIgor Mitsyanko .scan = qtnf_scan, 93398f44cb0SIgor Mitsyanko .connect = qtnf_connect, 9347c04b439SSergey Matyukevich .disconnect = qtnf_disconnect, 93527894448SSergey Matyukevich .dump_survey = qtnf_dump_survey, 93697883695SSergey Matyukevich .get_channel = qtnf_get_channel, 937b05ee456SIgor Mitsyanko .channel_switch = qtnf_channel_switch, 938b05ee456SIgor Mitsyanko .start_radar_detection = qtnf_start_radar_detection, 939f1398fd2SVasily Ulyanov .set_mac_acl = qtnf_set_mac_acl, 9404775ad06SSergei Maksimenko .set_power_mgmt = qtnf_set_power_mgmt, 94128b91884SSergey Matyukevich #ifdef CONFIG_PM 94228b91884SSergey Matyukevich .suspend = qtnf_suspend, 94328b91884SSergey Matyukevich .resume = qtnf_resume, 94428b91884SSergey Matyukevich .set_wakeup = qtnf_set_wakeup, 94528b91884SSergey Matyukevich #endif 94698f44cb0SIgor Mitsyanko }; 94798f44cb0SIgor Mitsyanko 9484dd07d2bSSergey Matyukevich static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, 94998f44cb0SIgor Mitsyanko struct regulatory_request *req) 95098f44cb0SIgor Mitsyanko { 9514dd07d2bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy_in); 9524dd07d2bSSergey Matyukevich struct qtnf_bus *bus = mac->bus; 9534dd07d2bSSergey Matyukevich struct wiphy *wiphy; 9544dd07d2bSSergey Matyukevich unsigned int mac_idx; 95598f44cb0SIgor Mitsyanko enum nl80211_band band; 9564dd07d2bSSergey Matyukevich int ret; 95798f44cb0SIgor Mitsyanko 95898f44cb0SIgor Mitsyanko pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, 95998f44cb0SIgor Mitsyanko req->alpha2[0], req->alpha2[1]); 96098f44cb0SIgor Mitsyanko 9614dd07d2bSSergey Matyukevich ret = qtnf_cmd_reg_notify(bus, req); 9624dd07d2bSSergey Matyukevich if (ret) { 9634dd07d2bSSergey Matyukevich if (ret != -EOPNOTSUPP && ret != -EALREADY) 9644dd07d2bSSergey Matyukevich pr_err("failed to update reg domain to %c%c\n", 9654dd07d2bSSergey Matyukevich req->alpha2[0], req->alpha2[1]); 96698f44cb0SIgor Mitsyanko return; 96798f44cb0SIgor Mitsyanko } 96898f44cb0SIgor Mitsyanko 9694dd07d2bSSergey Matyukevich for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { 9704dd07d2bSSergey Matyukevich if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) 97198f44cb0SIgor Mitsyanko continue; 97298f44cb0SIgor Mitsyanko 9734dd07d2bSSergey Matyukevich mac = bus->mac[mac_idx]; 9744fd045cdSIgor Mitsyanko if (!mac) 9754fd045cdSIgor Mitsyanko continue; 9764fd045cdSIgor Mitsyanko 9774dd07d2bSSergey Matyukevich wiphy = priv_to_wiphy(mac); 97898f44cb0SIgor Mitsyanko 97998f44cb0SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; ++band) { 98098f44cb0SIgor Mitsyanko if (!wiphy->bands[band]) 98198f44cb0SIgor Mitsyanko continue; 98298f44cb0SIgor Mitsyanko 983e294cbfdSIgor Mitsyanko ret = qtnf_cmd_band_info_get(mac, wiphy->bands[band]); 9844dd07d2bSSergey Matyukevich if (ret) 9854dd07d2bSSergey Matyukevich pr_err("failed to get chan info for mac %u band %u\n", 9864dd07d2bSSergey Matyukevich mac_idx, band); 98798f44cb0SIgor Mitsyanko } 98898f44cb0SIgor Mitsyanko } 98998f44cb0SIgor Mitsyanko } 99098f44cb0SIgor Mitsyanko 99198f44cb0SIgor Mitsyanko struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) 99298f44cb0SIgor Mitsyanko { 99398f44cb0SIgor Mitsyanko struct wiphy *wiphy; 99498f44cb0SIgor Mitsyanko 995fbb93020SDmitry Lebed if (bus->hw_info.hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD) 996fbb93020SDmitry Lebed qtn_cfg80211_ops.start_radar_detection = NULL; 997fbb93020SDmitry Lebed 9984775ad06SSergei Maksimenko if (!(bus->hw_info.hw_capab & QLINK_HW_CAPAB_PWR_MGMT)) 9994775ad06SSergei Maksimenko qtn_cfg80211_ops.set_power_mgmt = NULL; 10004775ad06SSergei Maksimenko 100198f44cb0SIgor Mitsyanko wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac)); 100298f44cb0SIgor Mitsyanko if (!wiphy) 100398f44cb0SIgor Mitsyanko return NULL; 100498f44cb0SIgor Mitsyanko 100598f44cb0SIgor Mitsyanko set_wiphy_dev(wiphy, bus->dev); 100698f44cb0SIgor Mitsyanko 100798f44cb0SIgor Mitsyanko return wiphy; 100898f44cb0SIgor Mitsyanko } 100998f44cb0SIgor Mitsyanko 1010537faf26SSergey Matyukevich static int 1011537faf26SSergey Matyukevich qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, struct qtnf_mac_info *mac_info) 101298f44cb0SIgor Mitsyanko { 1013537faf26SSergey Matyukevich struct ieee80211_iface_combination *if_comb; 1014537faf26SSergey Matyukevich size_t n_if_comb; 101598f44cb0SIgor Mitsyanko u16 interface_modes = 0; 1016537faf26SSergey Matyukevich size_t i, j; 101798f44cb0SIgor Mitsyanko 1018537faf26SSergey Matyukevich if_comb = mac_info->if_comb; 1019537faf26SSergey Matyukevich n_if_comb = mac_info->n_if_comb; 1020537faf26SSergey Matyukevich 1021537faf26SSergey Matyukevich if (!if_comb || !n_if_comb) 102298f44cb0SIgor Mitsyanko return -ENOENT; 102398f44cb0SIgor Mitsyanko 1024537faf26SSergey Matyukevich for (i = 0; i < n_if_comb; i++) { 1025537faf26SSergey Matyukevich if_comb[i].radar_detect_widths = mac_info->radar_detect_widths; 102698f44cb0SIgor Mitsyanko 1027537faf26SSergey Matyukevich for (j = 0; j < if_comb[i].n_limits; j++) 1028537faf26SSergey Matyukevich interface_modes |= if_comb[i].limits[j].types; 102998f44cb0SIgor Mitsyanko } 103098f44cb0SIgor Mitsyanko 1031537faf26SSergey Matyukevich wiphy->iface_combinations = if_comb; 1032537faf26SSergey Matyukevich wiphy->n_iface_combinations = n_if_comb; 103398f44cb0SIgor Mitsyanko wiphy->interface_modes = interface_modes; 103498f44cb0SIgor Mitsyanko 103598f44cb0SIgor Mitsyanko return 0; 103698f44cb0SIgor Mitsyanko } 103798f44cb0SIgor Mitsyanko 103898f44cb0SIgor Mitsyanko int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) 103998f44cb0SIgor Mitsyanko { 104098f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 1041d62b622cSSergey Matyukevich struct qtnf_mac_info *macinfo = &mac->macinfo; 104298f44cb0SIgor Mitsyanko int ret; 104398f44cb0SIgor Mitsyanko 104498f44cb0SIgor Mitsyanko if (!wiphy) { 104598f44cb0SIgor Mitsyanko pr_err("invalid wiphy pointer\n"); 104698f44cb0SIgor Mitsyanko return -EFAULT; 104798f44cb0SIgor Mitsyanko } 104898f44cb0SIgor Mitsyanko 1049d62b622cSSergey Matyukevich wiphy->frag_threshold = macinfo->frag_thr; 1050d62b622cSSergey Matyukevich wiphy->rts_threshold = macinfo->rts_thr; 1051d62b622cSSergey Matyukevich wiphy->retry_short = macinfo->sretry_limit; 1052d62b622cSSergey Matyukevich wiphy->retry_long = macinfo->lretry_limit; 1053d62b622cSSergey Matyukevich wiphy->coverage_class = macinfo->coverage_class; 105498f44cb0SIgor Mitsyanko 10558f1180e0SAndrey Shevchenko wiphy->max_scan_ssids = 10568f1180e0SAndrey Shevchenko (hw_info->max_scan_ssids) ? hw_info->max_scan_ssids : 1; 105798f44cb0SIgor Mitsyanko wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; 105898f44cb0SIgor Mitsyanko wiphy->mgmt_stypes = qtnf_mgmt_stypes; 105998f44cb0SIgor Mitsyanko wiphy->max_remain_on_channel_duration = 5000; 1060d62b622cSSergey Matyukevich wiphy->max_acl_mac_addrs = macinfo->max_acl_mac_addrs; 106197883695SSergey Matyukevich wiphy->max_num_csa_counters = 2; 106298f44cb0SIgor Mitsyanko 1063d62b622cSSergey Matyukevich ret = qtnf_wiphy_setup_if_comb(wiphy, macinfo); 1064537faf26SSergey Matyukevich if (ret) 1065537faf26SSergey Matyukevich goto out; 1066537faf26SSergey Matyukevich 106798f44cb0SIgor Mitsyanko /* Initialize cipher suits */ 106898f44cb0SIgor Mitsyanko wiphy->cipher_suites = qtnf_cipher_suites; 106998f44cb0SIgor Mitsyanko wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); 107098f44cb0SIgor Mitsyanko wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 107198f44cb0SIgor Mitsyanko wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | 107298f44cb0SIgor Mitsyanko WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 107397883695SSergey Matyukevich WIPHY_FLAG_AP_UAPSD | 107497883695SSergey Matyukevich WIPHY_FLAG_HAS_CHANNEL_SWITCH; 10754775ad06SSergei Maksimenko wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; 107698f44cb0SIgor Mitsyanko 1077fbb93020SDmitry Lebed if (hw_info->hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD) 1078fbb93020SDmitry Lebed wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD); 1079fbb93020SDmitry Lebed 108098f44cb0SIgor Mitsyanko wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 108198f44cb0SIgor Mitsyanko NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; 108298f44cb0SIgor Mitsyanko 1083d62b622cSSergey Matyukevich wiphy->available_antennas_tx = macinfo->num_tx_chain; 1084d62b622cSSergey Matyukevich wiphy->available_antennas_rx = macinfo->num_rx_chain; 108598f44cb0SIgor Mitsyanko 1086d62b622cSSergey Matyukevich wiphy->max_ap_assoc_sta = macinfo->max_ap_assoc_sta; 1087d62b622cSSergey Matyukevich wiphy->ht_capa_mod_mask = &macinfo->ht_cap_mod_mask; 1088d62b622cSSergey Matyukevich wiphy->vht_capa_mod_mask = &macinfo->vht_cap_mod_mask; 108998f44cb0SIgor Mitsyanko 109098f44cb0SIgor Mitsyanko ether_addr_copy(wiphy->perm_addr, mac->macaddr); 109198f44cb0SIgor Mitsyanko 1092db5c6d4aSIgor Mitsyanko if (hw_info->hw_capab & QLINK_HW_CAPAB_STA_INACT_TIMEOUT) 1093db5c6d4aSIgor Mitsyanko wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; 1094db5c6d4aSIgor Mitsyanko 10956fbef954SAndrey Shevchenko if (hw_info->hw_capab & QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR) 10966fbef954SAndrey Shevchenko wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; 10976fbef954SAndrey Shevchenko 109828b91884SSergey Matyukevich #ifdef CONFIG_PM 109928b91884SSergey Matyukevich if (macinfo->wowlan) 110028b91884SSergey Matyukevich wiphy->wowlan = macinfo->wowlan; 110128b91884SSergey Matyukevich #endif 110228b91884SSergey Matyukevich 1103db5c6d4aSIgor Mitsyanko if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) { 11044dd07d2bSSergey Matyukevich wiphy->regulatory_flags |= REGULATORY_STRICT_REG | 11054dd07d2bSSergey Matyukevich REGULATORY_CUSTOM_REG; 110698f44cb0SIgor Mitsyanko wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; 11074dd07d2bSSergey Matyukevich wiphy_apply_custom_regulatory(wiphy, hw_info->rd); 110898f44cb0SIgor Mitsyanko } else { 110998f44cb0SIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; 111098f44cb0SIgor Mitsyanko } 111198f44cb0SIgor Mitsyanko 11120b419d01SVasily Ulyanov strlcpy(wiphy->fw_version, hw_info->fw_version, 11130b419d01SVasily Ulyanov sizeof(wiphy->fw_version)); 11140b419d01SVasily Ulyanov wiphy->hw_version = hw_info->hw_version; 11150b419d01SVasily Ulyanov 111698f44cb0SIgor Mitsyanko ret = wiphy_register(wiphy); 1117ea19479fSSergey Matyukevich if (ret < 0) 1118ea19479fSSergey Matyukevich goto out; 1119ea19479fSSergey Matyukevich 1120ea19479fSSergey Matyukevich if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) 1121ea19479fSSergey Matyukevich ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); 1122ea19479fSSergey Matyukevich else if (isalpha(hw_info->rd->alpha2[0]) && 1123ea19479fSSergey Matyukevich isalpha(hw_info->rd->alpha2[1])) 1124ea19479fSSergey Matyukevich ret = regulatory_hint(wiphy, hw_info->rd->alpha2); 1125ea19479fSSergey Matyukevich 112698f44cb0SIgor Mitsyanko out: 112798f44cb0SIgor Mitsyanko return ret; 112898f44cb0SIgor Mitsyanko } 112998f44cb0SIgor Mitsyanko 113098f44cb0SIgor Mitsyanko void qtnf_netdev_updown(struct net_device *ndev, bool up) 113198f44cb0SIgor Mitsyanko { 113298f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 113398f44cb0SIgor Mitsyanko 113498f44cb0SIgor Mitsyanko if (qtnf_cmd_send_updown_intf(vif, up)) 113598f44cb0SIgor Mitsyanko pr_err("failed to send up/down command to FW\n"); 113698f44cb0SIgor Mitsyanko } 113798f44cb0SIgor Mitsyanko 113898f44cb0SIgor Mitsyanko void qtnf_virtual_intf_cleanup(struct net_device *ndev) 113998f44cb0SIgor Mitsyanko { 114098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 11414f8e2545SColin Ian King struct qtnf_wmac *mac = wiphy_priv(vif->wdev.wiphy); 114298f44cb0SIgor Mitsyanko 114398f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 1144263ee96bSSergey Matyukevich cfg80211_disconnected(vif->netdev, WLAN_REASON_DEAUTH_LEAVING, 114598f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 114698f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 114798f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 114898f44cb0SIgor Mitsyanko } 1149a715b3a0SSergey Matyukevich 1150a715b3a0SSergey Matyukevich qtnf_scan_done(mac, true); 115198f44cb0SIgor Mitsyanko } 115298f44cb0SIgor Mitsyanko 115398f44cb0SIgor Mitsyanko void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) 115498f44cb0SIgor Mitsyanko { 1155263ee96bSSergey Matyukevich if (vif->wdev.iftype == NL80211_IFTYPE_STATION) 1156263ee96bSSergey Matyukevich cfg80211_disconnected(vif->netdev, WLAN_REASON_DEAUTH_LEAVING, 115798f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 115898f44cb0SIgor Mitsyanko 115998f44cb0SIgor Mitsyanko cfg80211_shutdown_all_interfaces(vif->wdev.wiphy); 116098f44cb0SIgor Mitsyanko } 116198f44cb0SIgor Mitsyanko 116298f44cb0SIgor Mitsyanko void qtnf_band_init_rates(struct ieee80211_supported_band *band) 116398f44cb0SIgor Mitsyanko { 116498f44cb0SIgor Mitsyanko switch (band->band) { 116598f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 116698f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_2g; 116798f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_2g); 116898f44cb0SIgor Mitsyanko break; 116998f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 117098f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_5g; 117198f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_5g); 117298f44cb0SIgor Mitsyanko break; 117398f44cb0SIgor Mitsyanko default: 117498f44cb0SIgor Mitsyanko band->bitrates = NULL; 117598f44cb0SIgor Mitsyanko band->n_bitrates = 0; 117698f44cb0SIgor Mitsyanko break; 117798f44cb0SIgor Mitsyanko } 117898f44cb0SIgor Mitsyanko } 1179