1ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+ 2ff233cb5SSergey Matyukevich /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */ 398f44cb0SIgor Mitsyanko 498f44cb0SIgor Mitsyanko #include <linux/kernel.h> 598f44cb0SIgor Mitsyanko #include <linux/etherdevice.h> 698f44cb0SIgor Mitsyanko #include <linux/vmalloc.h> 798f44cb0SIgor Mitsyanko #include <linux/ieee80211.h> 898f44cb0SIgor Mitsyanko #include <net/cfg80211.h> 998f44cb0SIgor Mitsyanko #include <net/netlink.h> 1098f44cb0SIgor Mitsyanko 1198f44cb0SIgor Mitsyanko #include "cfg80211.h" 1298f44cb0SIgor Mitsyanko #include "commands.h" 1398f44cb0SIgor Mitsyanko #include "core.h" 1498f44cb0SIgor Mitsyanko #include "util.h" 1598f44cb0SIgor Mitsyanko #include "bus.h" 1698f44cb0SIgor Mitsyanko 1798f44cb0SIgor Mitsyanko /* Supported rates to be advertised to the cfg80211 */ 1898f44cb0SIgor Mitsyanko static struct ieee80211_rate qtnf_rates_2g[] = { 1998f44cb0SIgor Mitsyanko {.bitrate = 10, .hw_value = 2, }, 2098f44cb0SIgor Mitsyanko {.bitrate = 20, .hw_value = 4, }, 2198f44cb0SIgor Mitsyanko {.bitrate = 55, .hw_value = 11, }, 2298f44cb0SIgor Mitsyanko {.bitrate = 110, .hw_value = 22, }, 2398f44cb0SIgor Mitsyanko {.bitrate = 60, .hw_value = 12, }, 2498f44cb0SIgor Mitsyanko {.bitrate = 90, .hw_value = 18, }, 2598f44cb0SIgor Mitsyanko {.bitrate = 120, .hw_value = 24, }, 2698f44cb0SIgor Mitsyanko {.bitrate = 180, .hw_value = 36, }, 2798f44cb0SIgor Mitsyanko {.bitrate = 240, .hw_value = 48, }, 2898f44cb0SIgor Mitsyanko {.bitrate = 360, .hw_value = 72, }, 2998f44cb0SIgor Mitsyanko {.bitrate = 480, .hw_value = 96, }, 3098f44cb0SIgor Mitsyanko {.bitrate = 540, .hw_value = 108, }, 3198f44cb0SIgor Mitsyanko }; 3298f44cb0SIgor Mitsyanko 3398f44cb0SIgor Mitsyanko /* Supported rates to be advertised to the cfg80211 */ 3498f44cb0SIgor Mitsyanko static struct ieee80211_rate qtnf_rates_5g[] = { 3598f44cb0SIgor Mitsyanko {.bitrate = 60, .hw_value = 12, }, 3698f44cb0SIgor Mitsyanko {.bitrate = 90, .hw_value = 18, }, 3798f44cb0SIgor Mitsyanko {.bitrate = 120, .hw_value = 24, }, 3898f44cb0SIgor Mitsyanko {.bitrate = 180, .hw_value = 36, }, 3998f44cb0SIgor Mitsyanko {.bitrate = 240, .hw_value = 48, }, 4098f44cb0SIgor Mitsyanko {.bitrate = 360, .hw_value = 72, }, 4198f44cb0SIgor Mitsyanko {.bitrate = 480, .hw_value = 96, }, 4298f44cb0SIgor Mitsyanko {.bitrate = 540, .hw_value = 108, }, 4398f44cb0SIgor Mitsyanko }; 4498f44cb0SIgor Mitsyanko 4598f44cb0SIgor Mitsyanko /* Supported crypto cipher suits to be advertised to cfg80211 */ 4698f44cb0SIgor Mitsyanko static const u32 qtnf_cipher_suites[] = { 4798f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_TKIP, 4898f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_CCMP, 4998f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_AES_CMAC, 5098f44cb0SIgor Mitsyanko }; 5198f44cb0SIgor Mitsyanko 5298f44cb0SIgor Mitsyanko /* Supported mgmt frame types to be advertised to cfg80211 */ 5398f44cb0SIgor Mitsyanko static const struct ieee80211_txrx_stypes 5498f44cb0SIgor Mitsyanko qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = { 5598f44cb0SIgor Mitsyanko [NL80211_IFTYPE_STATION] = { 5647b08e75SSergey Matyukevich .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 5747b08e75SSergey Matyukevich BIT(IEEE80211_STYPE_AUTH >> 4), 5898f44cb0SIgor Mitsyanko .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 5947b08e75SSergey Matyukevich BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | 6047b08e75SSergey Matyukevich BIT(IEEE80211_STYPE_AUTH >> 4), 6198f44cb0SIgor Mitsyanko }, 6298f44cb0SIgor Mitsyanko [NL80211_IFTYPE_AP] = { 63b3860e7aSSergey Matyukevich .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | 64b3860e7aSSergey Matyukevich BIT(IEEE80211_STYPE_AUTH >> 4), 6598f44cb0SIgor Mitsyanko .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 663dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | 673dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | 683dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | 693dd06cecSSergey Matyukevich BIT(IEEE80211_STYPE_AUTH >> 4), 7098f44cb0SIgor Mitsyanko }, 7198f44cb0SIgor Mitsyanko }; 7298f44cb0SIgor Mitsyanko 7398f44cb0SIgor Mitsyanko static int 7401efff52SSergey Matyukevich qtnf_validate_iface_combinations(struct wiphy *wiphy, 7501efff52SSergey Matyukevich struct qtnf_vif *change_vif, 7601efff52SSergey Matyukevich enum nl80211_iftype new_type) 7701efff52SSergey Matyukevich { 7801efff52SSergey Matyukevich struct qtnf_wmac *mac; 7901efff52SSergey Matyukevich struct qtnf_vif *vif; 8001efff52SSergey Matyukevich int i; 8101efff52SSergey Matyukevich int ret = 0; 8201efff52SSergey Matyukevich struct iface_combination_params params = { 8301efff52SSergey Matyukevich .num_different_channels = 1, 8401efff52SSergey Matyukevich }; 8501efff52SSergey Matyukevich 8601efff52SSergey Matyukevich mac = wiphy_priv(wiphy); 8701efff52SSergey Matyukevich if (!mac) 8801efff52SSergey Matyukevich return -EFAULT; 8901efff52SSergey Matyukevich 9001efff52SSergey Matyukevich for (i = 0; i < QTNF_MAX_INTF; i++) { 9101efff52SSergey Matyukevich vif = &mac->iflist[i]; 9201efff52SSergey Matyukevich if (vif->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) 9301efff52SSergey Matyukevich params.iftype_num[vif->wdev.iftype]++; 9401efff52SSergey Matyukevich } 9501efff52SSergey Matyukevich 9601efff52SSergey Matyukevich if (change_vif) { 9701efff52SSergey Matyukevich params.iftype_num[new_type]++; 9801efff52SSergey Matyukevich params.iftype_num[change_vif->wdev.iftype]--; 9901efff52SSergey Matyukevich } else { 10001efff52SSergey Matyukevich params.iftype_num[new_type]++; 10101efff52SSergey Matyukevich } 10201efff52SSergey Matyukevich 10301efff52SSergey Matyukevich ret = cfg80211_check_combinations(wiphy, ¶ms); 10401efff52SSergey Matyukevich 1052d83dddfSDmitry Lebed if (ret) 1062d83dddfSDmitry Lebed return ret; 1072d83dddfSDmitry Lebed 1082d83dddfSDmitry Lebed /* Check repeater interface combination: primary VIF should be STA only. 1092d83dddfSDmitry Lebed * STA (primary) + AP (secondary) is OK. 1102d83dddfSDmitry Lebed * AP (primary) + STA (secondary) is not supported. 1112d83dddfSDmitry Lebed */ 1122d83dddfSDmitry Lebed vif = qtnf_mac_get_base_vif(mac); 1132d83dddfSDmitry Lebed if (vif && vif->wdev.iftype == NL80211_IFTYPE_AP && 1142d83dddfSDmitry Lebed vif != change_vif && new_type == NL80211_IFTYPE_STATION) { 1152d83dddfSDmitry Lebed ret = -EINVAL; 1162d83dddfSDmitry Lebed pr_err("MAC%u invalid combination: AP as primary repeater interface is not supported\n", 1172d83dddfSDmitry Lebed mac->macid); 1182d83dddfSDmitry Lebed } 1192d83dddfSDmitry Lebed 12001efff52SSergey Matyukevich return ret; 12101efff52SSergey Matyukevich } 12201efff52SSergey Matyukevich 12301efff52SSergey Matyukevich static int 12498f44cb0SIgor Mitsyanko qtnf_change_virtual_intf(struct wiphy *wiphy, 12598f44cb0SIgor Mitsyanko struct net_device *dev, 12698f44cb0SIgor Mitsyanko enum nl80211_iftype type, 12798f44cb0SIgor Mitsyanko struct vif_params *params) 12898f44cb0SIgor Mitsyanko { 12998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 130de624a35SSergey Matyukevich u8 *mac_addr = NULL; 131de624a35SSergey Matyukevich int use4addr = 0; 13298f44cb0SIgor Mitsyanko int ret; 13398f44cb0SIgor Mitsyanko 13401efff52SSergey Matyukevich ret = qtnf_validate_iface_combinations(wiphy, vif, type); 13501efff52SSergey Matyukevich if (ret) { 13601efff52SSergey Matyukevich pr_err("VIF%u.%u combination check: failed to set type %d\n", 13701efff52SSergey Matyukevich vif->mac->macid, vif->vifid, type); 13801efff52SSergey Matyukevich return ret; 13901efff52SSergey Matyukevich } 14001efff52SSergey Matyukevich 141de624a35SSergey Matyukevich if (params) { 14298f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 143de624a35SSergey Matyukevich use4addr = params->use_4addr; 144de624a35SSergey Matyukevich } 14598f44cb0SIgor Mitsyanko 14698f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 14798f44cb0SIgor Mitsyanko 148de624a35SSergey Matyukevich ret = qtnf_cmd_send_change_intf_type(vif, type, use4addr, mac_addr); 14998f44cb0SIgor Mitsyanko if (ret) { 150c6ed298fSSergey Matyukevich pr_err("VIF%u.%u: failed to change type to %d\n", 151c6ed298fSSergey Matyukevich vif->mac->macid, vif->vifid, type); 15298f44cb0SIgor Mitsyanko return ret; 15398f44cb0SIgor Mitsyanko } 15498f44cb0SIgor Mitsyanko 15598f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 15698f44cb0SIgor Mitsyanko return 0; 15798f44cb0SIgor Mitsyanko } 15898f44cb0SIgor Mitsyanko 15998f44cb0SIgor Mitsyanko int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) 16098f44cb0SIgor Mitsyanko { 16198f44cb0SIgor Mitsyanko struct net_device *netdev = wdev->netdev; 16298f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 163bc70732fSIgor Mitsyanko struct sk_buff *skb; 16498f44cb0SIgor Mitsyanko 16598f44cb0SIgor Mitsyanko if (WARN_ON(!netdev)) 16698f44cb0SIgor Mitsyanko return -EFAULT; 16798f44cb0SIgor Mitsyanko 16898f44cb0SIgor Mitsyanko vif = qtnf_netdev_get_priv(wdev->netdev); 16998f44cb0SIgor Mitsyanko 170a715b3a0SSergey Matyukevich qtnf_scan_done(vif->mac, true); 171a715b3a0SSergey Matyukevich 17298f44cb0SIgor Mitsyanko /* Stop data */ 17398f44cb0SIgor Mitsyanko netif_tx_stop_all_queues(netdev); 17498f44cb0SIgor Mitsyanko if (netif_carrier_ok(netdev)) 17598f44cb0SIgor Mitsyanko netif_carrier_off(netdev); 17698f44cb0SIgor Mitsyanko 177bc70732fSIgor Mitsyanko while ((skb = skb_dequeue(&vif->high_pri_tx_queue))) 178bc70732fSIgor Mitsyanko dev_kfree_skb_any(skb); 179bc70732fSIgor Mitsyanko 180bc70732fSIgor Mitsyanko cancel_work_sync(&vif->high_pri_tx_work); 181bc70732fSIgor Mitsyanko 18298f44cb0SIgor Mitsyanko if (netdev->reg_state == NETREG_REGISTERED) 18398f44cb0SIgor Mitsyanko unregister_netdevice(netdev); 18498f44cb0SIgor Mitsyanko 18587affddeSVasily Ulyanov if (qtnf_cmd_send_del_intf(vif)) 18687affddeSVasily Ulyanov pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, 18787affddeSVasily Ulyanov vif->vifid); 18887affddeSVasily Ulyanov 18998f44cb0SIgor Mitsyanko vif->netdev->ieee80211_ptr = NULL; 19098f44cb0SIgor Mitsyanko vif->netdev = NULL; 19198f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 19298f44cb0SIgor Mitsyanko 19398f44cb0SIgor Mitsyanko return 0; 19498f44cb0SIgor Mitsyanko } 19598f44cb0SIgor Mitsyanko 19698f44cb0SIgor Mitsyanko static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, 19798f44cb0SIgor Mitsyanko const char *name, 19898f44cb0SIgor Mitsyanko unsigned char name_assign_t, 19998f44cb0SIgor Mitsyanko enum nl80211_iftype type, 20098f44cb0SIgor Mitsyanko struct vif_params *params) 20198f44cb0SIgor Mitsyanko { 20298f44cb0SIgor Mitsyanko struct qtnf_wmac *mac; 20398f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 20498f44cb0SIgor Mitsyanko u8 *mac_addr = NULL; 205de624a35SSergey Matyukevich int use4addr = 0; 20601efff52SSergey Matyukevich int ret; 20798f44cb0SIgor Mitsyanko 20898f44cb0SIgor Mitsyanko mac = wiphy_priv(wiphy); 20998f44cb0SIgor Mitsyanko 21098f44cb0SIgor Mitsyanko if (!mac) 21198f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 21298f44cb0SIgor Mitsyanko 21301efff52SSergey Matyukevich ret = qtnf_validate_iface_combinations(wiphy, NULL, type); 21401efff52SSergey Matyukevich if (ret) { 21501efff52SSergey Matyukevich pr_err("MAC%u invalid combination: failed to add type %d\n", 21601efff52SSergey Matyukevich mac->macid, type); 21701efff52SSergey Matyukevich return ERR_PTR(ret); 21801efff52SSergey Matyukevich } 21901efff52SSergey Matyukevich 22098f44cb0SIgor Mitsyanko switch (type) { 22198f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 22298f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 22398f44cb0SIgor Mitsyanko vif = qtnf_mac_get_free_vif(mac); 22498f44cb0SIgor Mitsyanko if (!vif) { 22598f44cb0SIgor Mitsyanko pr_err("MAC%u: no free VIF available\n", mac->macid); 22698f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 22798f44cb0SIgor Mitsyanko } 22898f44cb0SIgor Mitsyanko 22998f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 2309a3beeb5SSergey Matyukevich eth_zero_addr(vif->bssid); 23198f44cb0SIgor Mitsyanko vif->bss_priority = QTNF_DEF_BSS_PRIORITY; 2329a3beeb5SSergey Matyukevich memset(&vif->wdev, 0, sizeof(vif->wdev)); 23398f44cb0SIgor Mitsyanko vif->wdev.wiphy = wiphy; 23498f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 23598f44cb0SIgor Mitsyanko break; 23698f44cb0SIgor Mitsyanko default: 23798f44cb0SIgor Mitsyanko pr_err("MAC%u: unsupported IF type %d\n", mac->macid, type); 23898f44cb0SIgor Mitsyanko return ERR_PTR(-ENOTSUPP); 23998f44cb0SIgor Mitsyanko } 24098f44cb0SIgor Mitsyanko 241de624a35SSergey Matyukevich if (params) { 24298f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 243de624a35SSergey Matyukevich use4addr = params->use_4addr; 244de624a35SSergey Matyukevich } 24598f44cb0SIgor Mitsyanko 246de624a35SSergey Matyukevich ret = qtnf_cmd_send_add_intf(vif, type, use4addr, mac_addr); 247c6ed298fSSergey Matyukevich if (ret) { 248c6ed298fSSergey Matyukevich pr_err("VIF%u.%u: failed to add VIF %pM\n", 249c6ed298fSSergey Matyukevich mac->macid, vif->vifid, mac_addr); 25098f44cb0SIgor Mitsyanko goto err_cmd; 25198f44cb0SIgor Mitsyanko } 25298f44cb0SIgor Mitsyanko 25398f44cb0SIgor Mitsyanko if (!is_valid_ether_addr(vif->mac_addr)) { 25498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: FW reported bad MAC: %pM\n", 25598f44cb0SIgor Mitsyanko mac->macid, vif->vifid, vif->mac_addr); 256c6ed298fSSergey Matyukevich ret = -EINVAL; 25745028223SIgor Mitsyanko goto error_del_vif; 25898f44cb0SIgor Mitsyanko } 25998f44cb0SIgor Mitsyanko 260c6ed298fSSergey Matyukevich ret = qtnf_core_net_attach(mac, vif, name, name_assign_t); 261c6ed298fSSergey Matyukevich if (ret) { 26298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid, 26398f44cb0SIgor Mitsyanko vif->vifid); 26445028223SIgor Mitsyanko goto error_del_vif; 26598f44cb0SIgor Mitsyanko } 26698f44cb0SIgor Mitsyanko 267310cd5ddSIgor Mitsyanko if (qtnf_hwcap_is_set(&mac->bus->hw_info, QLINK_HW_CAPAB_HW_BRIDGE)) { 268decfc5c7SIgor Mitsyanko ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex); 269decfc5c7SIgor Mitsyanko if (ret) { 270decfc5c7SIgor Mitsyanko unregister_netdevice(vif->netdev); 271decfc5c7SIgor Mitsyanko vif->netdev = NULL; 272decfc5c7SIgor Mitsyanko goto error_del_vif; 273decfc5c7SIgor Mitsyanko } 274decfc5c7SIgor Mitsyanko } 275decfc5c7SIgor Mitsyanko 27698f44cb0SIgor Mitsyanko vif->wdev.netdev = vif->netdev; 27798f44cb0SIgor Mitsyanko return &vif->wdev; 27898f44cb0SIgor Mitsyanko 27945028223SIgor Mitsyanko error_del_vif: 28098f44cb0SIgor Mitsyanko qtnf_cmd_send_del_intf(vif); 28198f44cb0SIgor Mitsyanko err_cmd: 28298f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 28398f44cb0SIgor Mitsyanko 284c6ed298fSSergey Matyukevich return ERR_PTR(ret); 28598f44cb0SIgor Mitsyanko } 28698f44cb0SIgor Mitsyanko 28798f44cb0SIgor Mitsyanko static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, 28898f44cb0SIgor Mitsyanko const struct cfg80211_beacon_data *info) 28998f44cb0SIgor Mitsyanko { 29098f44cb0SIgor Mitsyanko int ret = 0; 29198f44cb0SIgor Mitsyanko 29298f44cb0SIgor Mitsyanko if (!info->beacon_ies || !info->beacon_ies_len) { 2934d1f0fabSIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, 29498f44cb0SIgor Mitsyanko NULL, 0); 29598f44cb0SIgor Mitsyanko } else { 2964d1f0fabSIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, 29798f44cb0SIgor Mitsyanko info->beacon_ies, 29898f44cb0SIgor Mitsyanko info->beacon_ies_len); 29998f44cb0SIgor Mitsyanko } 30098f44cb0SIgor Mitsyanko 30198f44cb0SIgor Mitsyanko if (ret) 30298f44cb0SIgor Mitsyanko goto out; 30398f44cb0SIgor Mitsyanko 30498f44cb0SIgor Mitsyanko if (!info->proberesp_ies || !info->proberesp_ies_len) { 30598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 3064d1f0fabSIgor Mitsyanko QLINK_IE_SET_PROBE_RESP_IES, 30798f44cb0SIgor Mitsyanko NULL, 0); 30898f44cb0SIgor Mitsyanko } else { 30998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 3104d1f0fabSIgor Mitsyanko QLINK_IE_SET_PROBE_RESP_IES, 31198f44cb0SIgor Mitsyanko info->proberesp_ies, 31298f44cb0SIgor Mitsyanko info->proberesp_ies_len); 31398f44cb0SIgor Mitsyanko } 31498f44cb0SIgor Mitsyanko 31598f44cb0SIgor Mitsyanko if (ret) 31698f44cb0SIgor Mitsyanko goto out; 31798f44cb0SIgor Mitsyanko 31898f44cb0SIgor Mitsyanko if (!info->assocresp_ies || !info->assocresp_ies_len) { 31998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 3204d1f0fabSIgor Mitsyanko QLINK_IE_SET_ASSOC_RESP, 32198f44cb0SIgor Mitsyanko NULL, 0); 32298f44cb0SIgor Mitsyanko } else { 32398f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 3244d1f0fabSIgor Mitsyanko QLINK_IE_SET_ASSOC_RESP, 32598f44cb0SIgor Mitsyanko info->assocresp_ies, 32698f44cb0SIgor Mitsyanko info->assocresp_ies_len); 32798f44cb0SIgor Mitsyanko } 32898f44cb0SIgor Mitsyanko 32998f44cb0SIgor Mitsyanko out: 33098f44cb0SIgor Mitsyanko return ret; 33198f44cb0SIgor Mitsyanko } 33298f44cb0SIgor Mitsyanko 33398f44cb0SIgor Mitsyanko static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, 33498f44cb0SIgor Mitsyanko struct cfg80211_beacon_data *info) 33598f44cb0SIgor Mitsyanko { 33698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 33798f44cb0SIgor Mitsyanko 33898f44cb0SIgor Mitsyanko return qtnf_mgmt_set_appie(vif, info); 33998f44cb0SIgor Mitsyanko } 34098f44cb0SIgor Mitsyanko 34198f44cb0SIgor Mitsyanko static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, 34298f44cb0SIgor Mitsyanko struct cfg80211_ap_settings *settings) 34398f44cb0SIgor Mitsyanko { 34498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 34598f44cb0SIgor Mitsyanko int ret; 34698f44cb0SIgor Mitsyanko 34717011da0SIgor Mitsyanko ret = qtnf_cmd_send_start_ap(vif, settings); 348d7b80052SIgor Mitsyanko if (ret) 34998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, 35098f44cb0SIgor Mitsyanko vif->vifid); 35198f44cb0SIgor Mitsyanko 35298f44cb0SIgor Mitsyanko return ret; 35398f44cb0SIgor Mitsyanko } 35498f44cb0SIgor Mitsyanko 35598f44cb0SIgor Mitsyanko static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) 35698f44cb0SIgor Mitsyanko { 35798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 35898f44cb0SIgor Mitsyanko int ret; 35998f44cb0SIgor Mitsyanko 360a715b3a0SSergey Matyukevich qtnf_scan_done(vif->mac, true); 361a715b3a0SSergey Matyukevich 36298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_stop_ap(vif); 363c6ed298fSSergey Matyukevich if (ret) 36498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to stop AP operation in FW\n", 36598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 36698f44cb0SIgor Mitsyanko 36798f44cb0SIgor Mitsyanko netif_carrier_off(vif->netdev); 36898f44cb0SIgor Mitsyanko 36998f44cb0SIgor Mitsyanko return ret; 37098f44cb0SIgor Mitsyanko } 37198f44cb0SIgor Mitsyanko 37298f44cb0SIgor Mitsyanko static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed) 37398f44cb0SIgor Mitsyanko { 37498f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 37598f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 37698f44cb0SIgor Mitsyanko int ret; 37798f44cb0SIgor Mitsyanko 37898f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 37998f44cb0SIgor Mitsyanko if (!vif) { 38098f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 38198f44cb0SIgor Mitsyanko return -EFAULT; 38298f44cb0SIgor Mitsyanko } 38398f44cb0SIgor Mitsyanko 38498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_update_phy_params(mac, changed); 38598f44cb0SIgor Mitsyanko if (ret) 38698f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to update PHY params\n", mac->macid); 38798f44cb0SIgor Mitsyanko 38898f44cb0SIgor Mitsyanko return ret; 38998f44cb0SIgor Mitsyanko } 39098f44cb0SIgor Mitsyanko 39198f44cb0SIgor Mitsyanko static void 392*6cd536feSJohannes Berg qtnf_update_mgmt_frame_registrations(struct wiphy *wiphy, 393*6cd536feSJohannes Berg struct wireless_dev *wdev, 394*6cd536feSJohannes Berg struct mgmt_frame_regs *upd) 39598f44cb0SIgor Mitsyanko { 39698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 397*6cd536feSJohannes Berg u16 new_mask = upd->interface_stypes; 398*6cd536feSJohannes Berg u16 old_mask = vif->mgmt_frames_bitmask; 399*6cd536feSJohannes Berg static const struct { 400*6cd536feSJohannes Berg u16 mask, qlink_type; 401*6cd536feSJohannes Berg } updates[] = { 402*6cd536feSJohannes Berg { 403*6cd536feSJohannes Berg .mask = BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | 404*6cd536feSJohannes Berg BIT(IEEE80211_STYPE_ASSOC_REQ >> 4), 405*6cd536feSJohannes Berg .qlink_type = QLINK_MGMT_FRAME_ASSOC_REQ, 406*6cd536feSJohannes Berg }, 407*6cd536feSJohannes Berg { 408*6cd536feSJohannes Berg .mask = BIT(IEEE80211_STYPE_AUTH >> 4), 409*6cd536feSJohannes Berg .qlink_type = QLINK_MGMT_FRAME_AUTH, 410*6cd536feSJohannes Berg }, 411*6cd536feSJohannes Berg { 412*6cd536feSJohannes Berg .mask = BIT(IEEE80211_STYPE_PROBE_REQ >> 4), 413*6cd536feSJohannes Berg .qlink_type = QLINK_MGMT_FRAME_PROBE_REQ, 414*6cd536feSJohannes Berg }, 415*6cd536feSJohannes Berg { 416*6cd536feSJohannes Berg .mask = BIT(IEEE80211_STYPE_ACTION >> 4), 417*6cd536feSJohannes Berg .qlink_type = QLINK_MGMT_FRAME_ACTION, 418*6cd536feSJohannes Berg }, 419*6cd536feSJohannes Berg }; 420*6cd536feSJohannes Berg unsigned int i; 42198f44cb0SIgor Mitsyanko 422*6cd536feSJohannes Berg if (new_mask == old_mask) 42398f44cb0SIgor Mitsyanko return; 42498f44cb0SIgor Mitsyanko 425*6cd536feSJohannes Berg for (i = 0; i < ARRAY_SIZE(updates); i++) { 426*6cd536feSJohannes Berg u16 mask = updates[i].mask; 427*6cd536feSJohannes Berg u16 qlink_frame_type = updates[i].qlink_type; 428*6cd536feSJohannes Berg bool reg; 42998f44cb0SIgor Mitsyanko 430*6cd536feSJohannes Berg /* the ! are here due to the assoc/reassoc merge */ 431*6cd536feSJohannes Berg if (!(new_mask & mask) == !(old_mask & mask)) 432*6cd536feSJohannes Berg continue; 433*6cd536feSJohannes Berg 434*6cd536feSJohannes Berg reg = new_mask & mask; 435*6cd536feSJohannes Berg 436*6cd536feSJohannes Berg if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) 437*6cd536feSJohannes Berg pr_warn("VIF%u.%u: failed to %sregister qlink frame type 0x%x\n", 43898f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", 439*6cd536feSJohannes Berg qlink_frame_type); 44098f44cb0SIgor Mitsyanko } 44198f44cb0SIgor Mitsyanko 44298f44cb0SIgor Mitsyanko vif->mgmt_frames_bitmask = new_mask; 44398f44cb0SIgor Mitsyanko } 44498f44cb0SIgor Mitsyanko 44598f44cb0SIgor Mitsyanko static int 44698f44cb0SIgor Mitsyanko qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 44798f44cb0SIgor Mitsyanko struct cfg80211_mgmt_tx_params *params, u64 *cookie) 44898f44cb0SIgor Mitsyanko { 44998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 45098f44cb0SIgor Mitsyanko const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; 45198f44cb0SIgor Mitsyanko u32 short_cookie = prandom_u32(); 45298f44cb0SIgor Mitsyanko u16 flags = 0; 453e6e594afSIgor Mitsyanko u16 freq; 45498f44cb0SIgor Mitsyanko 45598f44cb0SIgor Mitsyanko *cookie = short_cookie; 45698f44cb0SIgor Mitsyanko 45798f44cb0SIgor Mitsyanko if (params->offchan) 458bc70732fSIgor Mitsyanko flags |= QLINK_FRAME_TX_FLAG_OFFCHAN; 45998f44cb0SIgor Mitsyanko 46098f44cb0SIgor Mitsyanko if (params->no_cck) 461bc70732fSIgor Mitsyanko flags |= QLINK_FRAME_TX_FLAG_NO_CCK; 46298f44cb0SIgor Mitsyanko 46398f44cb0SIgor Mitsyanko if (params->dont_wait_for_ack) 464bc70732fSIgor Mitsyanko flags |= QLINK_FRAME_TX_FLAG_ACK_NOWAIT; 46598f44cb0SIgor Mitsyanko 466e6e594afSIgor Mitsyanko /* If channel is not specified, pass "freq = 0" to tell device 467e6e594afSIgor Mitsyanko * firmware to use current channel. 468e6e594afSIgor Mitsyanko */ 469e6e594afSIgor Mitsyanko if (params->chan) 470e6e594afSIgor Mitsyanko freq = params->chan->center_freq; 471e6e594afSIgor Mitsyanko else 472e6e594afSIgor Mitsyanko freq = 0; 473e6e594afSIgor Mitsyanko 47498f44cb0SIgor Mitsyanko pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n", 475e6e594afSIgor Mitsyanko wdev->netdev->name, freq, 47698f44cb0SIgor Mitsyanko le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da, 47798f44cb0SIgor Mitsyanko params->len, short_cookie, flags); 47898f44cb0SIgor Mitsyanko 479bc70732fSIgor Mitsyanko return qtnf_cmd_send_frame(vif, short_cookie, flags, 480bc70732fSIgor Mitsyanko freq, params->buf, params->len); 48198f44cb0SIgor Mitsyanko } 48298f44cb0SIgor Mitsyanko 48398f44cb0SIgor Mitsyanko static int 48498f44cb0SIgor Mitsyanko qtnf_get_station(struct wiphy *wiphy, struct net_device *dev, 48598f44cb0SIgor Mitsyanko const u8 *mac, struct station_info *sinfo) 48698f44cb0SIgor Mitsyanko { 48798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 48898f44cb0SIgor Mitsyanko 4897a4d3a3bSIgor Mitsyanko sinfo->generation = vif->generation; 49098f44cb0SIgor Mitsyanko return qtnf_cmd_get_sta_info(vif, mac, sinfo); 49198f44cb0SIgor Mitsyanko } 49298f44cb0SIgor Mitsyanko 49398f44cb0SIgor Mitsyanko static int 49498f44cb0SIgor Mitsyanko qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev, 49598f44cb0SIgor Mitsyanko int idx, u8 *mac, struct station_info *sinfo) 49698f44cb0SIgor Mitsyanko { 49798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 49898f44cb0SIgor Mitsyanko const struct qtnf_sta_node *sta_node; 49998f44cb0SIgor Mitsyanko int ret; 50098f44cb0SIgor Mitsyanko 501e1c02eb1SSergey Matyukevich switch (vif->wdev.iftype) { 502e1c02eb1SSergey Matyukevich case NL80211_IFTYPE_STATION: 503e1c02eb1SSergey Matyukevich if (idx != 0 || !vif->wdev.current_bss) 504e1c02eb1SSergey Matyukevich return -ENOENT; 50598f44cb0SIgor Mitsyanko 506e1c02eb1SSergey Matyukevich ether_addr_copy(mac, vif->bssid); 507e1c02eb1SSergey Matyukevich break; 508e1c02eb1SSergey Matyukevich case NL80211_IFTYPE_AP: 509e1c02eb1SSergey Matyukevich sta_node = qtnf_sta_list_lookup_index(&vif->sta_list, idx); 51098f44cb0SIgor Mitsyanko if (unlikely(!sta_node)) 51198f44cb0SIgor Mitsyanko return -ENOENT; 51298f44cb0SIgor Mitsyanko 51398f44cb0SIgor Mitsyanko ether_addr_copy(mac, sta_node->mac_addr); 514e1c02eb1SSergey Matyukevich break; 515e1c02eb1SSergey Matyukevich default: 516e1c02eb1SSergey Matyukevich return -ENOTSUPP; 517e1c02eb1SSergey Matyukevich } 51898f44cb0SIgor Mitsyanko 519e1c02eb1SSergey Matyukevich ret = qtnf_cmd_get_sta_info(vif, mac, sinfo); 52098f44cb0SIgor Mitsyanko 521e1c02eb1SSergey Matyukevich if (vif->wdev.iftype == NL80211_IFTYPE_AP) { 522e1c02eb1SSergey Matyukevich if (ret == -ENOENT) { 52398f44cb0SIgor Mitsyanko cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL); 52498f44cb0SIgor Mitsyanko sinfo->filled = 0; 52598f44cb0SIgor Mitsyanko } 526e1c02eb1SSergey Matyukevich } 52798f44cb0SIgor Mitsyanko 5287a4d3a3bSIgor Mitsyanko sinfo->generation = vif->generation; 5297a4d3a3bSIgor Mitsyanko 53098f44cb0SIgor Mitsyanko return ret; 53198f44cb0SIgor Mitsyanko } 53298f44cb0SIgor Mitsyanko 53398f44cb0SIgor Mitsyanko static int qtnf_add_key(struct wiphy *wiphy, struct net_device *dev, 53498f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr, 53598f44cb0SIgor Mitsyanko struct key_params *params) 53698f44cb0SIgor Mitsyanko { 53798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 53898f44cb0SIgor Mitsyanko int ret; 53998f44cb0SIgor Mitsyanko 54098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params); 54198f44cb0SIgor Mitsyanko if (ret) 54298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add key: cipher=%x idx=%u pw=%u\n", 54398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->cipher, key_index, 54498f44cb0SIgor Mitsyanko pairwise); 54598f44cb0SIgor Mitsyanko 54698f44cb0SIgor Mitsyanko return ret; 54798f44cb0SIgor Mitsyanko } 54898f44cb0SIgor Mitsyanko 54998f44cb0SIgor Mitsyanko static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev, 55098f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr) 55198f44cb0SIgor Mitsyanko { 55298f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 55398f44cb0SIgor Mitsyanko int ret; 55498f44cb0SIgor Mitsyanko 55598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr); 55635da3fe6SSergey Matyukevich if (ret) { 55735da3fe6SSergey Matyukevich if (ret == -ENOENT) { 55835da3fe6SSergey Matyukevich pr_debug("VIF%u.%u: key index %d out of bounds\n", 55935da3fe6SSergey Matyukevich vif->mac->macid, vif->vifid, key_index); 56035da3fe6SSergey Matyukevich } else { 56198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n", 56235da3fe6SSergey Matyukevich vif->mac->macid, vif->vifid, 56335da3fe6SSergey Matyukevich key_index, pairwise); 56435da3fe6SSergey Matyukevich } 56535da3fe6SSergey Matyukevich } 56698f44cb0SIgor Mitsyanko 56798f44cb0SIgor Mitsyanko return ret; 56898f44cb0SIgor Mitsyanko } 56998f44cb0SIgor Mitsyanko 57098f44cb0SIgor Mitsyanko static int qtnf_set_default_key(struct wiphy *wiphy, struct net_device *dev, 57198f44cb0SIgor Mitsyanko u8 key_index, bool unicast, bool multicast) 57298f44cb0SIgor Mitsyanko { 57398f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 57498f44cb0SIgor Mitsyanko int ret; 57598f44cb0SIgor Mitsyanko 57698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_key(vif, key_index, unicast, multicast); 57798f44cb0SIgor Mitsyanko if (ret) 57898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set dflt key: idx=%u uc=%u mc=%u\n", 57998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, unicast, 58098f44cb0SIgor Mitsyanko multicast); 58198f44cb0SIgor Mitsyanko 58298f44cb0SIgor Mitsyanko return ret; 58398f44cb0SIgor Mitsyanko } 58498f44cb0SIgor Mitsyanko 58598f44cb0SIgor Mitsyanko static int 58698f44cb0SIgor Mitsyanko qtnf_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, 58798f44cb0SIgor Mitsyanko u8 key_index) 58898f44cb0SIgor Mitsyanko { 58998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 59098f44cb0SIgor Mitsyanko int ret; 59198f44cb0SIgor Mitsyanko 59298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_mgmt_key(vif, key_index); 59398f44cb0SIgor Mitsyanko if (ret) 59498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set default MGMT key: idx=%u\n", 59598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index); 59698f44cb0SIgor Mitsyanko 59798f44cb0SIgor Mitsyanko return ret; 59898f44cb0SIgor Mitsyanko } 59998f44cb0SIgor Mitsyanko 60098f44cb0SIgor Mitsyanko static int 60198f44cb0SIgor Mitsyanko qtnf_change_station(struct wiphy *wiphy, struct net_device *dev, 60298f44cb0SIgor Mitsyanko const u8 *mac, struct station_parameters *params) 60398f44cb0SIgor Mitsyanko { 60498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 60598f44cb0SIgor Mitsyanko int ret; 60698f44cb0SIgor Mitsyanko 60798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_sta(vif, mac, params); 60898f44cb0SIgor Mitsyanko if (ret) 60998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change STA %pM\n", 61098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, mac); 61198f44cb0SIgor Mitsyanko 61298f44cb0SIgor Mitsyanko return ret; 61398f44cb0SIgor Mitsyanko } 61498f44cb0SIgor Mitsyanko 61598f44cb0SIgor Mitsyanko static int 61698f44cb0SIgor Mitsyanko qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, 61798f44cb0SIgor Mitsyanko struct station_del_parameters *params) 61898f44cb0SIgor Mitsyanko { 61998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 62098f44cb0SIgor Mitsyanko int ret; 62198f44cb0SIgor Mitsyanko 62298f44cb0SIgor Mitsyanko if (params->mac && 62398f44cb0SIgor Mitsyanko (vif->wdev.iftype == NL80211_IFTYPE_AP) && 62498f44cb0SIgor Mitsyanko !is_broadcast_ether_addr(params->mac) && 62598f44cb0SIgor Mitsyanko !qtnf_sta_list_lookup(&vif->sta_list, params->mac)) 62698f44cb0SIgor Mitsyanko return 0; 62798f44cb0SIgor Mitsyanko 62898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_sta(vif, params); 62998f44cb0SIgor Mitsyanko if (ret) 63098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete STA %pM\n", 63198f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->mac); 632c6ed298fSSergey Matyukevich 63398f44cb0SIgor Mitsyanko return ret; 63498f44cb0SIgor Mitsyanko } 63598f44cb0SIgor Mitsyanko 63698f44cb0SIgor Mitsyanko static int 63798f44cb0SIgor Mitsyanko qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) 63898f44cb0SIgor Mitsyanko { 63998f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 640c6ed298fSSergey Matyukevich int ret; 64198f44cb0SIgor Mitsyanko 642f2cddd54SIgor Mitsyanko cancel_delayed_work_sync(&mac->scan_timeout); 643f2cddd54SIgor Mitsyanko 64498f44cb0SIgor Mitsyanko mac->scan_req = request; 64598f44cb0SIgor Mitsyanko 646c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_scan(mac); 647c6ed298fSSergey Matyukevich if (ret) { 64898f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to start scan\n", mac->macid); 649c7ead2abSSergey Matyukevich mac->scan_req = NULL; 650c6ed298fSSergey Matyukevich goto out; 651c7ead2abSSergey Matyukevich } 65298f44cb0SIgor Mitsyanko 653c6ed298fSSergey Matyukevich pr_debug("MAC%u: scan started\n", mac->macid); 654f2cddd54SIgor Mitsyanko queue_delayed_work(mac->bus->workqueue, &mac->scan_timeout, 655f2cddd54SIgor Mitsyanko QTNF_SCAN_TIMEOUT_SEC * HZ); 656c7ead2abSSergey Matyukevich 657c6ed298fSSergey Matyukevich out: 658c6ed298fSSergey Matyukevich return ret; 65998f44cb0SIgor Mitsyanko } 66098f44cb0SIgor Mitsyanko 66198f44cb0SIgor Mitsyanko static int 66298f44cb0SIgor Mitsyanko qtnf_connect(struct wiphy *wiphy, struct net_device *dev, 66398f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 66498f44cb0SIgor Mitsyanko { 66598f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 66698f44cb0SIgor Mitsyanko int ret; 66798f44cb0SIgor Mitsyanko 66898f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 66998f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 67098f44cb0SIgor Mitsyanko 67147b08e75SSergey Matyukevich if (sme->auth_type == NL80211_AUTHTYPE_SAE && 67247b08e75SSergey Matyukevich !(sme->flags & CONNECT_REQ_EXTERNAL_AUTH_SUPPORT)) { 67347b08e75SSergey Matyukevich pr_err("can not offload authentication to userspace\n"); 67447b08e75SSergey Matyukevich return -EOPNOTSUPP; 67547b08e75SSergey Matyukevich } 67647b08e75SSergey Matyukevich 67798f44cb0SIgor Mitsyanko if (sme->bssid) 6789766d1ddSIgor Mitsyanko ether_addr_copy(vif->bssid, sme->bssid); 67998f44cb0SIgor Mitsyanko else 6809766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 68198f44cb0SIgor Mitsyanko 68298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_connect(vif, sme); 68398f44cb0SIgor Mitsyanko if (ret) { 684c6ed298fSSergey Matyukevich pr_err("VIF%u.%u: failed to connect\n", 685c6ed298fSSergey Matyukevich vif->mac->macid, vif->vifid); 686c6ed298fSSergey Matyukevich goto out; 68798f44cb0SIgor Mitsyanko } 68898f44cb0SIgor Mitsyanko 689c6ed298fSSergey Matyukevich out: 690c6ed298fSSergey Matyukevich return ret; 69198f44cb0SIgor Mitsyanko } 69298f44cb0SIgor Mitsyanko 69398f44cb0SIgor Mitsyanko static int 69447b08e75SSergey Matyukevich qtnf_external_auth(struct wiphy *wiphy, struct net_device *dev, 69547b08e75SSergey Matyukevich struct cfg80211_external_auth_params *auth) 69647b08e75SSergey Matyukevich { 69747b08e75SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 69847b08e75SSergey Matyukevich int ret; 69947b08e75SSergey Matyukevich 700b3860e7aSSergey Matyukevich if (vif->wdev.iftype == NL80211_IFTYPE_STATION && 701b3860e7aSSergey Matyukevich !ether_addr_equal(vif->bssid, auth->bssid)) 70247b08e75SSergey Matyukevich pr_warn("unexpected bssid: %pM", auth->bssid); 70347b08e75SSergey Matyukevich 70447b08e75SSergey Matyukevich ret = qtnf_cmd_send_external_auth(vif, auth); 70547b08e75SSergey Matyukevich if (ret) { 70647b08e75SSergey Matyukevich pr_err("VIF%u.%u: failed to report external auth\n", 70747b08e75SSergey Matyukevich vif->mac->macid, vif->vifid); 70847b08e75SSergey Matyukevich goto out; 70947b08e75SSergey Matyukevich } 71047b08e75SSergey Matyukevich 71147b08e75SSergey Matyukevich out: 71247b08e75SSergey Matyukevich return ret; 71347b08e75SSergey Matyukevich } 71447b08e75SSergey Matyukevich 71547b08e75SSergey Matyukevich static int 71698f44cb0SIgor Mitsyanko qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, 71798f44cb0SIgor Mitsyanko u16 reason_code) 71898f44cb0SIgor Mitsyanko { 71998f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 72098f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 721480daa9cSSergey Matyukevich int ret = 0; 72298f44cb0SIgor Mitsyanko 72398f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 72498f44cb0SIgor Mitsyanko if (!vif) { 72598f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 726c1e3f64fSGustavo A. R. Silva return -EFAULT; 72798f44cb0SIgor Mitsyanko } 72898f44cb0SIgor Mitsyanko 729480daa9cSSergey Matyukevich if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { 730480daa9cSSergey Matyukevich ret = -EOPNOTSUPP; 731480daa9cSSergey Matyukevich goto out; 732480daa9cSSergey Matyukevich } 73398f44cb0SIgor Mitsyanko 73498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_disconnect(vif, reason_code); 735d5f693bcSIgor Mitsyanko if (ret) 736c6ed298fSSergey Matyukevich pr_err("VIF%u.%u: failed to disconnect\n", 737c6ed298fSSergey Matyukevich mac->macid, vif->vifid); 738d5f693bcSIgor Mitsyanko 739d5f693bcSIgor Mitsyanko if (vif->wdev.current_bss) { 740d5f693bcSIgor Mitsyanko netif_carrier_off(vif->netdev); 741d5f693bcSIgor Mitsyanko cfg80211_disconnected(vif->netdev, reason_code, 742d5f693bcSIgor Mitsyanko NULL, 0, true, GFP_KERNEL); 74398f44cb0SIgor Mitsyanko } 74498f44cb0SIgor Mitsyanko 745480daa9cSSergey Matyukevich out: 746480daa9cSSergey Matyukevich return ret; 74798f44cb0SIgor Mitsyanko } 74898f44cb0SIgor Mitsyanko 7497c04b439SSergey Matyukevich static int 7507c04b439SSergey Matyukevich qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, 7517c04b439SSergey Matyukevich int idx, struct survey_info *survey) 7527c04b439SSergey Matyukevich { 7537c04b439SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 754ef81e8e9SIgor Mitsyanko struct wireless_dev *wdev = dev->ieee80211_ptr; 7557c04b439SSergey Matyukevich struct ieee80211_supported_band *sband; 756ef81e8e9SIgor Mitsyanko const struct cfg80211_chan_def *chandef = &wdev->chandef; 7577c04b439SSergey Matyukevich struct ieee80211_channel *chan; 7587c04b439SSergey Matyukevich int ret; 7597c04b439SSergey Matyukevich 7607c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_2GHZ]; 7617c04b439SSergey Matyukevich if (sband && idx >= sband->n_channels) { 7627c04b439SSergey Matyukevich idx -= sband->n_channels; 7637c04b439SSergey Matyukevich sband = NULL; 7647c04b439SSergey Matyukevich } 7657c04b439SSergey Matyukevich 7667c04b439SSergey Matyukevich if (!sband) 7677c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_5GHZ]; 7687c04b439SSergey Matyukevich 7697c04b439SSergey Matyukevich if (!sband || idx >= sband->n_channels) 7707c04b439SSergey Matyukevich return -ENOENT; 7717c04b439SSergey Matyukevich 7727c04b439SSergey Matyukevich chan = &sband->channels[idx]; 7737c04b439SSergey Matyukevich survey->channel = chan; 7747c04b439SSergey Matyukevich survey->filled = 0x0; 7757c04b439SSergey Matyukevich 776601ce21fSIgor Mitsyanko if (chan == chandef->chan) 77734f1145bSSergey Matyukevich survey->filled = SURVEY_INFO_IN_USE; 77827894448SSergey Matyukevich 779601ce21fSIgor Mitsyanko ret = qtnf_cmd_get_chan_stats(mac, chan->center_freq, survey); 780601ce21fSIgor Mitsyanko if (ret) 7817c04b439SSergey Matyukevich pr_debug("failed to get chan(%d) stats from card\n", 7827c04b439SSergey Matyukevich chan->hw_value); 7837c04b439SSergey Matyukevich 7847c04b439SSergey Matyukevich return ret; 7857c04b439SSergey Matyukevich } 7867c04b439SSergey Matyukevich 78727894448SSergey Matyukevich static int 78827894448SSergey Matyukevich qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, 78927894448SSergey Matyukevich struct cfg80211_chan_def *chandef) 79027894448SSergey Matyukevich { 79127894448SSergey Matyukevich struct net_device *ndev = wdev->netdev; 79227894448SSergey Matyukevich struct qtnf_vif *vif; 7939e5478b6SIgor Mitsyanko int ret; 79427894448SSergey Matyukevich 79527894448SSergey Matyukevich if (!ndev) 79627894448SSergey Matyukevich return -ENODEV; 79727894448SSergey Matyukevich 79827894448SSergey Matyukevich vif = qtnf_netdev_get_priv(wdev->netdev); 79927894448SSergey Matyukevich 8009e5478b6SIgor Mitsyanko ret = qtnf_cmd_get_channel(vif, chandef); 8019e5478b6SIgor Mitsyanko if (ret) { 8029e5478b6SIgor Mitsyanko pr_err("%s: failed to get channel: %d\n", ndev->name, ret); 803c6ed298fSSergey Matyukevich ret = -ENODATA; 8049e5478b6SIgor Mitsyanko goto out; 80527894448SSergey Matyukevich } 80627894448SSergey Matyukevich 8079e5478b6SIgor Mitsyanko if (!cfg80211_chandef_valid(chandef)) { 8085bf374abSSergey Matyukevich pr_err("%s: bad channel freq=%u cf1=%u cf2=%u bw=%u\n", 8095bf374abSSergey Matyukevich ndev->name, chandef->chan->center_freq, 8109e5478b6SIgor Mitsyanko chandef->center_freq1, chandef->center_freq2, 8119e5478b6SIgor Mitsyanko chandef->width); 8129e5478b6SIgor Mitsyanko ret = -ENODATA; 813c6ed298fSSergey Matyukevich goto out; 81434f1145bSSergey Matyukevich } 81534f1145bSSergey Matyukevich 8169e5478b6SIgor Mitsyanko out: 8179e5478b6SIgor Mitsyanko return ret; 81827894448SSergey Matyukevich } 81927894448SSergey Matyukevich 82097883695SSergey Matyukevich static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, 82197883695SSergey Matyukevich struct cfg80211_csa_settings *params) 82297883695SSergey Matyukevich { 82397883695SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 82497883695SSergey Matyukevich int ret; 82597883695SSergey Matyukevich 82697883695SSergey Matyukevich pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, 82797883695SSergey Matyukevich params->chandef.chan->hw_value, params->count, 82897883695SSergey Matyukevich params->radar_required, params->block_tx); 82997883695SSergey Matyukevich 83097883695SSergey Matyukevich if (!cfg80211_chandef_valid(¶ms->chandef)) { 83197883695SSergey Matyukevich pr_err("%s: invalid channel\n", dev->name); 83297883695SSergey Matyukevich return -EINVAL; 83397883695SSergey Matyukevich } 83497883695SSergey Matyukevich 8358c015b90SIgor Mitsyanko ret = qtnf_cmd_send_chan_switch(vif, params); 83697883695SSergey Matyukevich if (ret) 83797883695SSergey Matyukevich pr_warn("%s: failed to switch to channel (%u)\n", 83897883695SSergey Matyukevich dev->name, params->chandef.chan->hw_value); 83997883695SSergey Matyukevich 84097883695SSergey Matyukevich return ret; 84197883695SSergey Matyukevich } 84297883695SSergey Matyukevich 843b05ee456SIgor Mitsyanko static int qtnf_start_radar_detection(struct wiphy *wiphy, 844b05ee456SIgor Mitsyanko struct net_device *ndev, 845b05ee456SIgor Mitsyanko struct cfg80211_chan_def *chandef, 846b05ee456SIgor Mitsyanko u32 cac_time_ms) 847b05ee456SIgor Mitsyanko { 848b05ee456SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 849b05ee456SIgor Mitsyanko int ret; 850b05ee456SIgor Mitsyanko 851fbb93020SDmitry Lebed if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) 852fbb93020SDmitry Lebed return -ENOTSUPP; 853fbb93020SDmitry Lebed 854b05ee456SIgor Mitsyanko ret = qtnf_cmd_start_cac(vif, chandef, cac_time_ms); 855b05ee456SIgor Mitsyanko if (ret) 856b05ee456SIgor Mitsyanko pr_err("%s: failed to start CAC ret=%d\n", ndev->name, ret); 857b05ee456SIgor Mitsyanko 858b05ee456SIgor Mitsyanko return ret; 859b05ee456SIgor Mitsyanko } 860b05ee456SIgor Mitsyanko 861f1398fd2SVasily Ulyanov static int qtnf_set_mac_acl(struct wiphy *wiphy, 862f1398fd2SVasily Ulyanov struct net_device *dev, 863f1398fd2SVasily Ulyanov const struct cfg80211_acl_data *params) 864f1398fd2SVasily Ulyanov { 865f1398fd2SVasily Ulyanov struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 866f1398fd2SVasily Ulyanov int ret; 867f1398fd2SVasily Ulyanov 868f1398fd2SVasily Ulyanov ret = qtnf_cmd_set_mac_acl(vif, params); 869f1398fd2SVasily Ulyanov if (ret) 870f1398fd2SVasily Ulyanov pr_err("%s: failed to set mac ACL ret=%d\n", dev->name, ret); 871f1398fd2SVasily Ulyanov 872f1398fd2SVasily Ulyanov return ret; 873f1398fd2SVasily Ulyanov } 874f1398fd2SVasily Ulyanov 8754775ad06SSergei Maksimenko static int qtnf_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, 8764775ad06SSergei Maksimenko bool enabled, int timeout) 8774775ad06SSergei Maksimenko { 8784775ad06SSergei Maksimenko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 8794775ad06SSergei Maksimenko int ret; 8804775ad06SSergei Maksimenko 8814775ad06SSergei Maksimenko ret = qtnf_cmd_send_pm_set(vif, enabled ? QLINK_PM_AUTO_STANDBY : 8824775ad06SSergei Maksimenko QLINK_PM_OFF, timeout); 883c6ed298fSSergey Matyukevich if (ret) 8844775ad06SSergei Maksimenko pr_err("%s: failed to set PM mode ret=%d\n", dev->name, ret); 8854775ad06SSergei Maksimenko 8864775ad06SSergei Maksimenko return ret; 8874775ad06SSergei Maksimenko } 8884775ad06SSergei Maksimenko 8890756e913SMikhail Karpenko static int qtnf_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 8900756e913SMikhail Karpenko int *dbm) 8910756e913SMikhail Karpenko { 8920756e913SMikhail Karpenko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 8930756e913SMikhail Karpenko int ret; 8940756e913SMikhail Karpenko 8950756e913SMikhail Karpenko ret = qtnf_cmd_get_tx_power(vif, dbm); 8960756e913SMikhail Karpenko if (ret) 8970756e913SMikhail Karpenko pr_err("MAC%u: failed to get Tx power\n", vif->mac->macid); 8980756e913SMikhail Karpenko 8990756e913SMikhail Karpenko return ret; 9000756e913SMikhail Karpenko } 9010756e913SMikhail Karpenko 9020756e913SMikhail Karpenko static int qtnf_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 9030756e913SMikhail Karpenko enum nl80211_tx_power_setting type, int mbm) 9040756e913SMikhail Karpenko { 9050756e913SMikhail Karpenko struct qtnf_vif *vif; 9060756e913SMikhail Karpenko int ret; 9070756e913SMikhail Karpenko 9080756e913SMikhail Karpenko if (wdev) { 9090756e913SMikhail Karpenko vif = qtnf_netdev_get_priv(wdev->netdev); 9100756e913SMikhail Karpenko } else { 9110756e913SMikhail Karpenko struct qtnf_wmac *mac = wiphy_priv(wiphy); 9120756e913SMikhail Karpenko 9130756e913SMikhail Karpenko vif = qtnf_mac_get_base_vif(mac); 9140756e913SMikhail Karpenko if (!vif) { 9150756e913SMikhail Karpenko pr_err("MAC%u: primary VIF is not configured\n", 9160756e913SMikhail Karpenko mac->macid); 9170756e913SMikhail Karpenko return -EFAULT; 9180756e913SMikhail Karpenko } 9190756e913SMikhail Karpenko } 9200756e913SMikhail Karpenko 9210756e913SMikhail Karpenko ret = qtnf_cmd_set_tx_power(vif, type, mbm); 9220756e913SMikhail Karpenko if (ret) 9230756e913SMikhail Karpenko pr_err("MAC%u: failed to set Tx power\n", vif->mac->macid); 9240756e913SMikhail Karpenko 9250756e913SMikhail Karpenko return ret; 9260756e913SMikhail Karpenko } 9270756e913SMikhail Karpenko 92844d09764SSergey Matyukevich static int qtnf_update_owe_info(struct wiphy *wiphy, struct net_device *dev, 92944d09764SSergey Matyukevich struct cfg80211_update_owe_info *owe_info) 93044d09764SSergey Matyukevich { 93144d09764SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 93244d09764SSergey Matyukevich int ret; 93344d09764SSergey Matyukevich 93444d09764SSergey Matyukevich if (vif->wdev.iftype != NL80211_IFTYPE_AP) 93544d09764SSergey Matyukevich return -EOPNOTSUPP; 93644d09764SSergey Matyukevich 93744d09764SSergey Matyukevich ret = qtnf_cmd_send_update_owe(vif, owe_info); 93844d09764SSergey Matyukevich if (ret) { 93944d09764SSergey Matyukevich pr_err("VIF%u.%u: failed to update owe info\n", 94044d09764SSergey Matyukevich vif->mac->macid, vif->vifid); 94144d09764SSergey Matyukevich goto out; 94244d09764SSergey Matyukevich } 94344d09764SSergey Matyukevich 94444d09764SSergey Matyukevich out: 94544d09764SSergey Matyukevich return ret; 94644d09764SSergey Matyukevich } 94744d09764SSergey Matyukevich 94828b91884SSergey Matyukevich #ifdef CONFIG_PM 94928b91884SSergey Matyukevich static int qtnf_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) 95028b91884SSergey Matyukevich { 95128b91884SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 95228b91884SSergey Matyukevich struct qtnf_vif *vif; 95328b91884SSergey Matyukevich int ret = 0; 95428b91884SSergey Matyukevich 95528b91884SSergey Matyukevich vif = qtnf_mac_get_base_vif(mac); 95628b91884SSergey Matyukevich if (!vif) { 95728b91884SSergey Matyukevich pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 95828b91884SSergey Matyukevich ret = -EFAULT; 95928b91884SSergey Matyukevich goto exit; 96028b91884SSergey Matyukevich } 96128b91884SSergey Matyukevich 96228b91884SSergey Matyukevich if (!wowlan) { 96328b91884SSergey Matyukevich pr_debug("WoWLAN triggers are not enabled\n"); 96428b91884SSergey Matyukevich qtnf_virtual_intf_cleanup(vif->netdev); 96528b91884SSergey Matyukevich goto exit; 96628b91884SSergey Matyukevich } 96728b91884SSergey Matyukevich 96828b91884SSergey Matyukevich qtnf_scan_done(vif->mac, true); 96928b91884SSergey Matyukevich 97028b91884SSergey Matyukevich ret = qtnf_cmd_send_wowlan_set(vif, wowlan); 97128b91884SSergey Matyukevich if (ret) { 97228b91884SSergey Matyukevich pr_err("MAC%u: failed to set WoWLAN triggers\n", 97328b91884SSergey Matyukevich mac->macid); 97428b91884SSergey Matyukevich goto exit; 97528b91884SSergey Matyukevich } 97628b91884SSergey Matyukevich 97728b91884SSergey Matyukevich exit: 97828b91884SSergey Matyukevich return ret; 97928b91884SSergey Matyukevich } 98028b91884SSergey Matyukevich 98128b91884SSergey Matyukevich static int qtnf_resume(struct wiphy *wiphy) 98228b91884SSergey Matyukevich { 98328b91884SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 98428b91884SSergey Matyukevich struct qtnf_vif *vif; 98528b91884SSergey Matyukevich int ret = 0; 98628b91884SSergey Matyukevich 98728b91884SSergey Matyukevich vif = qtnf_mac_get_base_vif(mac); 98828b91884SSergey Matyukevich if (!vif) { 98928b91884SSergey Matyukevich pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 99028b91884SSergey Matyukevich ret = -EFAULT; 99128b91884SSergey Matyukevich goto exit; 99228b91884SSergey Matyukevich } 99328b91884SSergey Matyukevich 99428b91884SSergey Matyukevich ret = qtnf_cmd_send_wowlan_set(vif, NULL); 99528b91884SSergey Matyukevich if (ret) { 99628b91884SSergey Matyukevich pr_err("MAC%u: failed to reset WoWLAN triggers\n", 99728b91884SSergey Matyukevich mac->macid); 99828b91884SSergey Matyukevich goto exit; 99928b91884SSergey Matyukevich } 100028b91884SSergey Matyukevich 100128b91884SSergey Matyukevich exit: 100228b91884SSergey Matyukevich return ret; 100328b91884SSergey Matyukevich } 100428b91884SSergey Matyukevich 100528b91884SSergey Matyukevich static void qtnf_set_wakeup(struct wiphy *wiphy, bool enabled) 100628b91884SSergey Matyukevich { 100728b91884SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 100828b91884SSergey Matyukevich struct qtnf_bus *bus = mac->bus; 100928b91884SSergey Matyukevich 101028b91884SSergey Matyukevich device_set_wakeup_enable(bus->dev, enabled); 101128b91884SSergey Matyukevich } 101228b91884SSergey Matyukevich #endif 101328b91884SSergey Matyukevich 101498f44cb0SIgor Mitsyanko static struct cfg80211_ops qtn_cfg80211_ops = { 101598f44cb0SIgor Mitsyanko .add_virtual_intf = qtnf_add_virtual_intf, 101698f44cb0SIgor Mitsyanko .change_virtual_intf = qtnf_change_virtual_intf, 101798f44cb0SIgor Mitsyanko .del_virtual_intf = qtnf_del_virtual_intf, 101898f44cb0SIgor Mitsyanko .start_ap = qtnf_start_ap, 101998f44cb0SIgor Mitsyanko .change_beacon = qtnf_change_beacon, 102098f44cb0SIgor Mitsyanko .stop_ap = qtnf_stop_ap, 102198f44cb0SIgor Mitsyanko .set_wiphy_params = qtnf_set_wiphy_params, 1022*6cd536feSJohannes Berg .update_mgmt_frame_registrations = 1023*6cd536feSJohannes Berg qtnf_update_mgmt_frame_registrations, 102498f44cb0SIgor Mitsyanko .mgmt_tx = qtnf_mgmt_tx, 102598f44cb0SIgor Mitsyanko .change_station = qtnf_change_station, 102698f44cb0SIgor Mitsyanko .del_station = qtnf_del_station, 102798f44cb0SIgor Mitsyanko .get_station = qtnf_get_station, 102898f44cb0SIgor Mitsyanko .dump_station = qtnf_dump_station, 102998f44cb0SIgor Mitsyanko .add_key = qtnf_add_key, 103098f44cb0SIgor Mitsyanko .del_key = qtnf_del_key, 103198f44cb0SIgor Mitsyanko .set_default_key = qtnf_set_default_key, 103298f44cb0SIgor Mitsyanko .set_default_mgmt_key = qtnf_set_default_mgmt_key, 103398f44cb0SIgor Mitsyanko .scan = qtnf_scan, 103498f44cb0SIgor Mitsyanko .connect = qtnf_connect, 103547b08e75SSergey Matyukevich .external_auth = qtnf_external_auth, 10367c04b439SSergey Matyukevich .disconnect = qtnf_disconnect, 103727894448SSergey Matyukevich .dump_survey = qtnf_dump_survey, 103897883695SSergey Matyukevich .get_channel = qtnf_get_channel, 1039b05ee456SIgor Mitsyanko .channel_switch = qtnf_channel_switch, 1040b05ee456SIgor Mitsyanko .start_radar_detection = qtnf_start_radar_detection, 1041f1398fd2SVasily Ulyanov .set_mac_acl = qtnf_set_mac_acl, 10424775ad06SSergei Maksimenko .set_power_mgmt = qtnf_set_power_mgmt, 10430756e913SMikhail Karpenko .get_tx_power = qtnf_get_tx_power, 10440756e913SMikhail Karpenko .set_tx_power = qtnf_set_tx_power, 104544d09764SSergey Matyukevich .update_owe_info = qtnf_update_owe_info, 104628b91884SSergey Matyukevich #ifdef CONFIG_PM 104728b91884SSergey Matyukevich .suspend = qtnf_suspend, 104828b91884SSergey Matyukevich .resume = qtnf_resume, 104928b91884SSergey Matyukevich .set_wakeup = qtnf_set_wakeup, 105028b91884SSergey Matyukevich #endif 105198f44cb0SIgor Mitsyanko }; 105298f44cb0SIgor Mitsyanko 1053d1231721SIgor Mitsyanko static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy, 105498f44cb0SIgor Mitsyanko struct regulatory_request *req) 105598f44cb0SIgor Mitsyanko { 1056d1231721SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 105798f44cb0SIgor Mitsyanko enum nl80211_band band; 10584dd07d2bSSergey Matyukevich int ret; 105998f44cb0SIgor Mitsyanko 106098f44cb0SIgor Mitsyanko pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, 106198f44cb0SIgor Mitsyanko req->alpha2[0], req->alpha2[1]); 106298f44cb0SIgor Mitsyanko 1063155b424cSSergey Matyukevich ret = qtnf_cmd_reg_notify(mac, req, qtnf_slave_radar_get(), 1064155b424cSSergey Matyukevich qtnf_dfs_offload_get()); 10654dd07d2bSSergey Matyukevich if (ret) { 1066642f15a5SIgor Mitsyanko pr_err("MAC%u: failed to update region to %c%c: %d\n", 1067642f15a5SIgor Mitsyanko mac->macid, req->alpha2[0], req->alpha2[1], ret); 106898f44cb0SIgor Mitsyanko return; 106998f44cb0SIgor Mitsyanko } 107098f44cb0SIgor Mitsyanko 107198f44cb0SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; ++band) { 107298f44cb0SIgor Mitsyanko if (!wiphy->bands[band]) 107398f44cb0SIgor Mitsyanko continue; 107498f44cb0SIgor Mitsyanko 1075e294cbfdSIgor Mitsyanko ret = qtnf_cmd_band_info_get(mac, wiphy->bands[band]); 10764dd07d2bSSergey Matyukevich if (ret) 1077d1231721SIgor Mitsyanko pr_err("MAC%u: failed to update band %u\n", 1078d1231721SIgor Mitsyanko mac->macid, band); 107998f44cb0SIgor Mitsyanko } 108098f44cb0SIgor Mitsyanko } 108198f44cb0SIgor Mitsyanko 1082616f5701SSergey Matyukevich struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus, 1083616f5701SSergey Matyukevich struct platform_device *pdev) 108498f44cb0SIgor Mitsyanko { 108598f44cb0SIgor Mitsyanko struct wiphy *wiphy; 108698f44cb0SIgor Mitsyanko 1087155b424cSSergey Matyukevich if (qtnf_dfs_offload_get() && 1088310cd5ddSIgor Mitsyanko qtnf_hwcap_is_set(&bus->hw_info, QLINK_HW_CAPAB_DFS_OFFLOAD)) 1089fbb93020SDmitry Lebed qtn_cfg80211_ops.start_radar_detection = NULL; 1090fbb93020SDmitry Lebed 1091310cd5ddSIgor Mitsyanko if (!qtnf_hwcap_is_set(&bus->hw_info, QLINK_HW_CAPAB_PWR_MGMT)) 10924775ad06SSergei Maksimenko qtn_cfg80211_ops.set_power_mgmt = NULL; 10934775ad06SSergei Maksimenko 109498f44cb0SIgor Mitsyanko wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac)); 109598f44cb0SIgor Mitsyanko if (!wiphy) 109698f44cb0SIgor Mitsyanko return NULL; 109798f44cb0SIgor Mitsyanko 1098616f5701SSergey Matyukevich if (pdev) 1099616f5701SSergey Matyukevich set_wiphy_dev(wiphy, &pdev->dev); 1100616f5701SSergey Matyukevich else 110198f44cb0SIgor Mitsyanko set_wiphy_dev(wiphy, bus->dev); 110298f44cb0SIgor Mitsyanko 110398f44cb0SIgor Mitsyanko return wiphy; 110498f44cb0SIgor Mitsyanko } 110598f44cb0SIgor Mitsyanko 1106537faf26SSergey Matyukevich static int 1107537faf26SSergey Matyukevich qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, struct qtnf_mac_info *mac_info) 110898f44cb0SIgor Mitsyanko { 1109537faf26SSergey Matyukevich struct ieee80211_iface_combination *if_comb; 1110537faf26SSergey Matyukevich size_t n_if_comb; 111198f44cb0SIgor Mitsyanko u16 interface_modes = 0; 1112537faf26SSergey Matyukevich size_t i, j; 111398f44cb0SIgor Mitsyanko 1114537faf26SSergey Matyukevich if_comb = mac_info->if_comb; 1115537faf26SSergey Matyukevich n_if_comb = mac_info->n_if_comb; 1116537faf26SSergey Matyukevich 1117537faf26SSergey Matyukevich if (!if_comb || !n_if_comb) 111898f44cb0SIgor Mitsyanko return -ENOENT; 111998f44cb0SIgor Mitsyanko 1120537faf26SSergey Matyukevich for (i = 0; i < n_if_comb; i++) { 1121537faf26SSergey Matyukevich if_comb[i].radar_detect_widths = mac_info->radar_detect_widths; 112298f44cb0SIgor Mitsyanko 1123537faf26SSergey Matyukevich for (j = 0; j < if_comb[i].n_limits; j++) 1124537faf26SSergey Matyukevich interface_modes |= if_comb[i].limits[j].types; 112598f44cb0SIgor Mitsyanko } 112698f44cb0SIgor Mitsyanko 1127537faf26SSergey Matyukevich wiphy->iface_combinations = if_comb; 1128537faf26SSergey Matyukevich wiphy->n_iface_combinations = n_if_comb; 112998f44cb0SIgor Mitsyanko wiphy->interface_modes = interface_modes; 113098f44cb0SIgor Mitsyanko 113198f44cb0SIgor Mitsyanko return 0; 113298f44cb0SIgor Mitsyanko } 113398f44cb0SIgor Mitsyanko 113498f44cb0SIgor Mitsyanko int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) 113598f44cb0SIgor Mitsyanko { 113698f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 1137d62b622cSSergey Matyukevich struct qtnf_mac_info *macinfo = &mac->macinfo; 113898f44cb0SIgor Mitsyanko int ret; 113948cefdfbSIgor Mitsyanko bool regdomain_is_known; 114098f44cb0SIgor Mitsyanko 114198f44cb0SIgor Mitsyanko if (!wiphy) { 114298f44cb0SIgor Mitsyanko pr_err("invalid wiphy pointer\n"); 114398f44cb0SIgor Mitsyanko return -EFAULT; 114498f44cb0SIgor Mitsyanko } 114598f44cb0SIgor Mitsyanko 1146d62b622cSSergey Matyukevich wiphy->frag_threshold = macinfo->frag_thr; 1147d62b622cSSergey Matyukevich wiphy->rts_threshold = macinfo->rts_thr; 1148d62b622cSSergey Matyukevich wiphy->retry_short = macinfo->sretry_limit; 1149d62b622cSSergey Matyukevich wiphy->retry_long = macinfo->lretry_limit; 1150d62b622cSSergey Matyukevich wiphy->coverage_class = macinfo->coverage_class; 115198f44cb0SIgor Mitsyanko 11528f1180e0SAndrey Shevchenko wiphy->max_scan_ssids = 11530d18a9c0SIgor Mitsyanko (macinfo->max_scan_ssids) ? macinfo->max_scan_ssids : 1; 115498f44cb0SIgor Mitsyanko wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; 115598f44cb0SIgor Mitsyanko wiphy->mgmt_stypes = qtnf_mgmt_stypes; 115698f44cb0SIgor Mitsyanko wiphy->max_remain_on_channel_duration = 5000; 1157d62b622cSSergey Matyukevich wiphy->max_acl_mac_addrs = macinfo->max_acl_mac_addrs; 115897883695SSergey Matyukevich wiphy->max_num_csa_counters = 2; 115998f44cb0SIgor Mitsyanko 1160d62b622cSSergey Matyukevich ret = qtnf_wiphy_setup_if_comb(wiphy, macinfo); 1161537faf26SSergey Matyukevich if (ret) 1162537faf26SSergey Matyukevich goto out; 1163537faf26SSergey Matyukevich 116498f44cb0SIgor Mitsyanko /* Initialize cipher suits */ 116598f44cb0SIgor Mitsyanko wiphy->cipher_suites = qtnf_cipher_suites; 116698f44cb0SIgor Mitsyanko wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); 116798f44cb0SIgor Mitsyanko wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 116898f44cb0SIgor Mitsyanko wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | 116998f44cb0SIgor Mitsyanko WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 117097883695SSergey Matyukevich WIPHY_FLAG_AP_UAPSD | 1171de624a35SSergey Matyukevich WIPHY_FLAG_HAS_CHANNEL_SWITCH | 117272b3270eSSergey Matyukevich WIPHY_FLAG_4ADDR_STATION | 117372b3270eSSergey Matyukevich WIPHY_FLAG_NETNS_OK; 11744775ad06SSergei Maksimenko wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; 117598f44cb0SIgor Mitsyanko 1176155b424cSSergey Matyukevich if (qtnf_dfs_offload_get() && 1177310cd5ddSIgor Mitsyanko qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_DFS_OFFLOAD)) 1178fbb93020SDmitry Lebed wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD); 1179fbb93020SDmitry Lebed 1180310cd5ddSIgor Mitsyanko if (qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_SCAN_DWELL)) 11812525f188SSergey Matyukevich wiphy_ext_feature_set(wiphy, 11822525f188SSergey Matyukevich NL80211_EXT_FEATURE_SET_SCAN_DWELL); 11832525f188SSergey Matyukevich 118498f44cb0SIgor Mitsyanko wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 118598f44cb0SIgor Mitsyanko NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; 118698f44cb0SIgor Mitsyanko 1187d62b622cSSergey Matyukevich wiphy->available_antennas_tx = macinfo->num_tx_chain; 1188d62b622cSSergey Matyukevich wiphy->available_antennas_rx = macinfo->num_rx_chain; 118998f44cb0SIgor Mitsyanko 1190d62b622cSSergey Matyukevich wiphy->max_ap_assoc_sta = macinfo->max_ap_assoc_sta; 1191d62b622cSSergey Matyukevich wiphy->ht_capa_mod_mask = &macinfo->ht_cap_mod_mask; 1192d62b622cSSergey Matyukevich wiphy->vht_capa_mod_mask = &macinfo->vht_cap_mod_mask; 119398f44cb0SIgor Mitsyanko 119498f44cb0SIgor Mitsyanko ether_addr_copy(wiphy->perm_addr, mac->macaddr); 119598f44cb0SIgor Mitsyanko 1196310cd5ddSIgor Mitsyanko if (qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_STA_INACT_TIMEOUT)) 1197db5c6d4aSIgor Mitsyanko wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; 1198db5c6d4aSIgor Mitsyanko 1199310cd5ddSIgor Mitsyanko if (qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_SCAN_RANDOM_MAC_ADDR)) 12006fbef954SAndrey Shevchenko wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; 12016fbef954SAndrey Shevchenko 1202310cd5ddSIgor Mitsyanko if (!qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_OBSS_SCAN)) 120392246b12SIgor Mitsyanko wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN; 120492246b12SIgor Mitsyanko 1205310cd5ddSIgor Mitsyanko if (qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_SAE)) 120647b08e75SSergey Matyukevich wiphy->features |= NL80211_FEATURE_SAE; 120747b08e75SSergey Matyukevich 120828b91884SSergey Matyukevich #ifdef CONFIG_PM 120928b91884SSergey Matyukevich if (macinfo->wowlan) 121028b91884SSergey Matyukevich wiphy->wowlan = macinfo->wowlan; 121128b91884SSergey Matyukevich #endif 121228b91884SSergey Matyukevich 1213c698bce0SIgor Mitsyanko regdomain_is_known = isalpha(mac->rd->alpha2[0]) && 1214c698bce0SIgor Mitsyanko isalpha(mac->rd->alpha2[1]); 121548cefdfbSIgor Mitsyanko 1216310cd5ddSIgor Mitsyanko if (qtnf_hwcap_is_set(hw_info, QLINK_HW_CAPAB_REG_UPDATE)) { 121798f44cb0SIgor Mitsyanko wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; 121848cefdfbSIgor Mitsyanko 1219c698bce0SIgor Mitsyanko if (mac->rd->alpha2[0] == '9' && mac->rd->alpha2[1] == '9') { 122048cefdfbSIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | 122148cefdfbSIgor Mitsyanko REGULATORY_STRICT_REG; 1222c698bce0SIgor Mitsyanko wiphy_apply_custom_regulatory(wiphy, mac->rd); 122348cefdfbSIgor Mitsyanko } else if (regdomain_is_known) { 122448cefdfbSIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_STRICT_REG; 122548cefdfbSIgor Mitsyanko } 122698f44cb0SIgor Mitsyanko } else { 122798f44cb0SIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; 122898f44cb0SIgor Mitsyanko } 122998f44cb0SIgor Mitsyanko 1230ab1c64a1SSergey Matyukevich if (mac->macinfo.extended_capabilities_len) { 1231ab1c64a1SSergey Matyukevich wiphy->extended_capabilities = 1232ab1c64a1SSergey Matyukevich mac->macinfo.extended_capabilities; 1233ab1c64a1SSergey Matyukevich wiphy->extended_capabilities_mask = 1234ab1c64a1SSergey Matyukevich mac->macinfo.extended_capabilities_mask; 1235ab1c64a1SSergey Matyukevich wiphy->extended_capabilities_len = 1236ab1c64a1SSergey Matyukevich mac->macinfo.extended_capabilities_len; 1237ab1c64a1SSergey Matyukevich } 1238ab1c64a1SSergey Matyukevich 12390b419d01SVasily Ulyanov strlcpy(wiphy->fw_version, hw_info->fw_version, 12400b419d01SVasily Ulyanov sizeof(wiphy->fw_version)); 12410b419d01SVasily Ulyanov wiphy->hw_version = hw_info->hw_version; 12420b419d01SVasily Ulyanov 124398f44cb0SIgor Mitsyanko ret = wiphy_register(wiphy); 1244ea19479fSSergey Matyukevich if (ret < 0) 1245ea19479fSSergey Matyukevich goto out; 1246ea19479fSSergey Matyukevich 1247ea19479fSSergey Matyukevich if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) 1248c698bce0SIgor Mitsyanko ret = regulatory_set_wiphy_regd(wiphy, mac->rd); 124948cefdfbSIgor Mitsyanko else if (regdomain_is_known) 1250c698bce0SIgor Mitsyanko ret = regulatory_hint(wiphy, mac->rd->alpha2); 1251ea19479fSSergey Matyukevich 125298f44cb0SIgor Mitsyanko out: 125398f44cb0SIgor Mitsyanko return ret; 125498f44cb0SIgor Mitsyanko } 125598f44cb0SIgor Mitsyanko 125698f44cb0SIgor Mitsyanko void qtnf_netdev_updown(struct net_device *ndev, bool up) 125798f44cb0SIgor Mitsyanko { 125898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 125998f44cb0SIgor Mitsyanko 126098f44cb0SIgor Mitsyanko if (qtnf_cmd_send_updown_intf(vif, up)) 1261c6ed298fSSergey Matyukevich pr_err("failed to send %s command to VIF%u.%u\n", 1262c6ed298fSSergey Matyukevich up ? "UP" : "DOWN", vif->mac->macid, vif->vifid); 126398f44cb0SIgor Mitsyanko } 126498f44cb0SIgor Mitsyanko 126598f44cb0SIgor Mitsyanko void qtnf_virtual_intf_cleanup(struct net_device *ndev) 126698f44cb0SIgor Mitsyanko { 126798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 12684f8e2545SColin Ian King struct qtnf_wmac *mac = wiphy_priv(vif->wdev.wiphy); 126998f44cb0SIgor Mitsyanko 1270d5f693bcSIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) 127198f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 127298f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 1273a715b3a0SSergey Matyukevich 1274a715b3a0SSergey Matyukevich qtnf_scan_done(mac, true); 127598f44cb0SIgor Mitsyanko } 127698f44cb0SIgor Mitsyanko 127798f44cb0SIgor Mitsyanko void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) 127898f44cb0SIgor Mitsyanko { 1279263ee96bSSergey Matyukevich if (vif->wdev.iftype == NL80211_IFTYPE_STATION) 1280263ee96bSSergey Matyukevich cfg80211_disconnected(vif->netdev, WLAN_REASON_DEAUTH_LEAVING, 128198f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 128298f44cb0SIgor Mitsyanko 128398f44cb0SIgor Mitsyanko cfg80211_shutdown_all_interfaces(vif->wdev.wiphy); 128498f44cb0SIgor Mitsyanko } 128598f44cb0SIgor Mitsyanko 128698f44cb0SIgor Mitsyanko void qtnf_band_init_rates(struct ieee80211_supported_band *band) 128798f44cb0SIgor Mitsyanko { 128898f44cb0SIgor Mitsyanko switch (band->band) { 128998f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 129098f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_2g; 129198f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_2g); 129298f44cb0SIgor Mitsyanko break; 129398f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 129498f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_5g; 129598f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_5g); 129698f44cb0SIgor Mitsyanko break; 129798f44cb0SIgor Mitsyanko default: 129898f44cb0SIgor Mitsyanko band->bitrates = NULL; 129998f44cb0SIgor Mitsyanko band->n_bitrates = 0; 130098f44cb0SIgor Mitsyanko break; 130198f44cb0SIgor Mitsyanko } 130298f44cb0SIgor Mitsyanko } 1303