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 8498f44cb0SIgor Mitsyanko qtnf_change_virtual_intf(struct wiphy *wiphy, 8598f44cb0SIgor Mitsyanko struct net_device *dev, 8698f44cb0SIgor Mitsyanko enum nl80211_iftype type, 8798f44cb0SIgor Mitsyanko struct vif_params *params) 8898f44cb0SIgor Mitsyanko { 8998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 9098f44cb0SIgor Mitsyanko u8 *mac_addr; 9198f44cb0SIgor Mitsyanko int ret; 9298f44cb0SIgor Mitsyanko 9398f44cb0SIgor Mitsyanko if (params) 9498f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 9598f44cb0SIgor Mitsyanko else 9698f44cb0SIgor Mitsyanko mac_addr = NULL; 9798f44cb0SIgor Mitsyanko 9898f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 9998f44cb0SIgor Mitsyanko 10098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_intf_type(vif, type, mac_addr); 10198f44cb0SIgor Mitsyanko if (ret) { 10298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change VIF type: %d\n", 10398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, ret); 10498f44cb0SIgor Mitsyanko return ret; 10598f44cb0SIgor Mitsyanko } 10698f44cb0SIgor Mitsyanko 10798f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 10898f44cb0SIgor Mitsyanko return 0; 10998f44cb0SIgor Mitsyanko } 11098f44cb0SIgor Mitsyanko 11198f44cb0SIgor Mitsyanko int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) 11298f44cb0SIgor Mitsyanko { 11398f44cb0SIgor Mitsyanko struct net_device *netdev = wdev->netdev; 11498f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 11598f44cb0SIgor Mitsyanko 11698f44cb0SIgor Mitsyanko if (WARN_ON(!netdev)) 11798f44cb0SIgor Mitsyanko return -EFAULT; 11898f44cb0SIgor Mitsyanko 11998f44cb0SIgor Mitsyanko vif = qtnf_netdev_get_priv(wdev->netdev); 12098f44cb0SIgor Mitsyanko 121a715b3a0SSergey Matyukevich qtnf_scan_done(vif->mac, true); 122a715b3a0SSergey Matyukevich 12398f44cb0SIgor Mitsyanko /* Stop data */ 12498f44cb0SIgor Mitsyanko netif_tx_stop_all_queues(netdev); 12598f44cb0SIgor Mitsyanko if (netif_carrier_ok(netdev)) 12698f44cb0SIgor Mitsyanko netif_carrier_off(netdev); 12798f44cb0SIgor Mitsyanko 12898f44cb0SIgor Mitsyanko if (netdev->reg_state == NETREG_REGISTERED) 12998f44cb0SIgor Mitsyanko unregister_netdevice(netdev); 13098f44cb0SIgor Mitsyanko 13187affddeSVasily Ulyanov if (qtnf_cmd_send_del_intf(vif)) 13287affddeSVasily Ulyanov pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, 13387affddeSVasily Ulyanov vif->vifid); 13487affddeSVasily Ulyanov 13598f44cb0SIgor Mitsyanko vif->netdev->ieee80211_ptr = NULL; 13698f44cb0SIgor Mitsyanko vif->netdev = NULL; 13798f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 13898f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 1399766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 14098f44cb0SIgor Mitsyanko 14198f44cb0SIgor Mitsyanko return 0; 14298f44cb0SIgor Mitsyanko } 14398f44cb0SIgor Mitsyanko 14498f44cb0SIgor Mitsyanko static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, 14598f44cb0SIgor Mitsyanko const char *name, 14698f44cb0SIgor Mitsyanko unsigned char name_assign_t, 14798f44cb0SIgor Mitsyanko enum nl80211_iftype type, 14898f44cb0SIgor Mitsyanko struct vif_params *params) 14998f44cb0SIgor Mitsyanko { 15098f44cb0SIgor Mitsyanko struct qtnf_wmac *mac; 15198f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 15298f44cb0SIgor Mitsyanko u8 *mac_addr = NULL; 15398f44cb0SIgor Mitsyanko 15498f44cb0SIgor Mitsyanko mac = wiphy_priv(wiphy); 15598f44cb0SIgor Mitsyanko 15698f44cb0SIgor Mitsyanko if (!mac) 15798f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 15898f44cb0SIgor Mitsyanko 15998f44cb0SIgor Mitsyanko switch (type) { 16098f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 16198f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 16298f44cb0SIgor Mitsyanko vif = qtnf_mac_get_free_vif(mac); 16398f44cb0SIgor Mitsyanko if (!vif) { 16498f44cb0SIgor Mitsyanko pr_err("MAC%u: no free VIF available\n", mac->macid); 16598f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 16698f44cb0SIgor Mitsyanko } 16798f44cb0SIgor Mitsyanko 16898f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 16998f44cb0SIgor Mitsyanko vif->bss_priority = QTNF_DEF_BSS_PRIORITY; 17098f44cb0SIgor Mitsyanko vif->wdev.wiphy = wiphy; 17198f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 17298f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 17398f44cb0SIgor Mitsyanko break; 17498f44cb0SIgor Mitsyanko default: 17598f44cb0SIgor Mitsyanko pr_err("MAC%u: unsupported IF type %d\n", mac->macid, type); 17698f44cb0SIgor Mitsyanko return ERR_PTR(-ENOTSUPP); 17798f44cb0SIgor Mitsyanko } 17898f44cb0SIgor Mitsyanko 17998f44cb0SIgor Mitsyanko if (params) 18098f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 18198f44cb0SIgor Mitsyanko 18298f44cb0SIgor Mitsyanko if (qtnf_cmd_send_add_intf(vif, type, mac_addr)) { 18398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add VIF\n", mac->macid, vif->vifid); 18498f44cb0SIgor Mitsyanko goto err_cmd; 18598f44cb0SIgor Mitsyanko } 18698f44cb0SIgor Mitsyanko 18798f44cb0SIgor Mitsyanko if (!is_valid_ether_addr(vif->mac_addr)) { 18898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: FW reported bad MAC: %pM\n", 18998f44cb0SIgor Mitsyanko mac->macid, vif->vifid, vif->mac_addr); 19098f44cb0SIgor Mitsyanko goto err_mac; 19198f44cb0SIgor Mitsyanko } 19298f44cb0SIgor Mitsyanko 193e6ef8cd0SIgor Mitsyanko if (qtnf_core_net_attach(mac, vif, name, name_assign_t)) { 19498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid, 19598f44cb0SIgor Mitsyanko vif->vifid); 19698f44cb0SIgor Mitsyanko goto err_net; 19798f44cb0SIgor Mitsyanko } 19898f44cb0SIgor Mitsyanko 19998f44cb0SIgor Mitsyanko vif->wdev.netdev = vif->netdev; 20098f44cb0SIgor Mitsyanko return &vif->wdev; 20198f44cb0SIgor Mitsyanko 20298f44cb0SIgor Mitsyanko err_net: 20398f44cb0SIgor Mitsyanko vif->netdev = NULL; 20498f44cb0SIgor Mitsyanko err_mac: 20598f44cb0SIgor Mitsyanko qtnf_cmd_send_del_intf(vif); 20698f44cb0SIgor Mitsyanko err_cmd: 20798f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 2089766d1ddSIgor Mitsyanko eth_zero_addr(vif->mac_addr); 2099766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 21098f44cb0SIgor Mitsyanko 21198f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 21298f44cb0SIgor Mitsyanko } 21398f44cb0SIgor Mitsyanko 21498f44cb0SIgor Mitsyanko static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, 21598f44cb0SIgor Mitsyanko const struct cfg80211_beacon_data *info) 21698f44cb0SIgor Mitsyanko { 21798f44cb0SIgor Mitsyanko int ret = 0; 21898f44cb0SIgor Mitsyanko 21998f44cb0SIgor Mitsyanko if (!info->beacon_ies || !info->beacon_ies_len) { 2204d1f0fabSIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, 22198f44cb0SIgor Mitsyanko NULL, 0); 22298f44cb0SIgor Mitsyanko } else { 2234d1f0fabSIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, 22498f44cb0SIgor Mitsyanko info->beacon_ies, 22598f44cb0SIgor Mitsyanko info->beacon_ies_len); 22698f44cb0SIgor Mitsyanko } 22798f44cb0SIgor Mitsyanko 22898f44cb0SIgor Mitsyanko if (ret) 22998f44cb0SIgor Mitsyanko goto out; 23098f44cb0SIgor Mitsyanko 23198f44cb0SIgor Mitsyanko if (!info->proberesp_ies || !info->proberesp_ies_len) { 23298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2334d1f0fabSIgor Mitsyanko QLINK_IE_SET_PROBE_RESP_IES, 23498f44cb0SIgor Mitsyanko NULL, 0); 23598f44cb0SIgor Mitsyanko } else { 23698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2374d1f0fabSIgor Mitsyanko QLINK_IE_SET_PROBE_RESP_IES, 23898f44cb0SIgor Mitsyanko info->proberesp_ies, 23998f44cb0SIgor Mitsyanko info->proberesp_ies_len); 24098f44cb0SIgor Mitsyanko } 24198f44cb0SIgor Mitsyanko 24298f44cb0SIgor Mitsyanko if (ret) 24398f44cb0SIgor Mitsyanko goto out; 24498f44cb0SIgor Mitsyanko 24598f44cb0SIgor Mitsyanko if (!info->assocresp_ies || !info->assocresp_ies_len) { 24698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2474d1f0fabSIgor Mitsyanko QLINK_IE_SET_ASSOC_RESP, 24898f44cb0SIgor Mitsyanko NULL, 0); 24998f44cb0SIgor Mitsyanko } else { 25098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 2514d1f0fabSIgor Mitsyanko QLINK_IE_SET_ASSOC_RESP, 25298f44cb0SIgor Mitsyanko info->assocresp_ies, 25398f44cb0SIgor Mitsyanko info->assocresp_ies_len); 25498f44cb0SIgor Mitsyanko } 25598f44cb0SIgor Mitsyanko 25698f44cb0SIgor Mitsyanko out: 25798f44cb0SIgor Mitsyanko return ret; 25898f44cb0SIgor Mitsyanko } 25998f44cb0SIgor Mitsyanko 26098f44cb0SIgor Mitsyanko static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, 26198f44cb0SIgor Mitsyanko struct cfg80211_beacon_data *info) 26298f44cb0SIgor Mitsyanko { 26398f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 26498f44cb0SIgor Mitsyanko 26598f44cb0SIgor Mitsyanko return qtnf_mgmt_set_appie(vif, info); 26698f44cb0SIgor Mitsyanko } 26798f44cb0SIgor Mitsyanko 26898f44cb0SIgor Mitsyanko static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, 26998f44cb0SIgor Mitsyanko struct cfg80211_ap_settings *settings) 27098f44cb0SIgor Mitsyanko { 27198f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 27298f44cb0SIgor Mitsyanko int ret; 27398f44cb0SIgor Mitsyanko 27417011da0SIgor Mitsyanko ret = qtnf_cmd_send_start_ap(vif, settings); 275d7b80052SIgor Mitsyanko if (ret) 27698f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, 27798f44cb0SIgor Mitsyanko vif->vifid); 27898f44cb0SIgor Mitsyanko 27998f44cb0SIgor Mitsyanko return ret; 28098f44cb0SIgor Mitsyanko } 28198f44cb0SIgor Mitsyanko 28298f44cb0SIgor Mitsyanko static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) 28398f44cb0SIgor Mitsyanko { 28498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 28598f44cb0SIgor Mitsyanko int ret; 28698f44cb0SIgor Mitsyanko 287a715b3a0SSergey Matyukevich qtnf_scan_done(vif->mac, true); 288a715b3a0SSergey Matyukevich 28998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_stop_ap(vif); 29098f44cb0SIgor Mitsyanko if (ret) { 29198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to stop AP operation in FW\n", 29298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 29398f44cb0SIgor Mitsyanko 29498f44cb0SIgor Mitsyanko netif_carrier_off(vif->netdev); 29598f44cb0SIgor Mitsyanko } 29698f44cb0SIgor Mitsyanko 29798f44cb0SIgor Mitsyanko return ret; 29898f44cb0SIgor Mitsyanko } 29998f44cb0SIgor Mitsyanko 30098f44cb0SIgor Mitsyanko static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed) 30198f44cb0SIgor Mitsyanko { 30298f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 30398f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 30498f44cb0SIgor Mitsyanko int ret; 30598f44cb0SIgor Mitsyanko 30698f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 30798f44cb0SIgor Mitsyanko if (!vif) { 30898f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 30998f44cb0SIgor Mitsyanko return -EFAULT; 31098f44cb0SIgor Mitsyanko } 31198f44cb0SIgor Mitsyanko 31298f44cb0SIgor Mitsyanko if (changed & (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) { 31398f44cb0SIgor Mitsyanko pr_err("MAC%u: can't modify retry params\n", mac->macid); 31498f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 31598f44cb0SIgor Mitsyanko } 31698f44cb0SIgor Mitsyanko 31798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_update_phy_params(mac, changed); 31898f44cb0SIgor Mitsyanko if (ret) 31998f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to update PHY params\n", mac->macid); 32098f44cb0SIgor Mitsyanko 32198f44cb0SIgor Mitsyanko return ret; 32298f44cb0SIgor Mitsyanko } 32398f44cb0SIgor Mitsyanko 32498f44cb0SIgor Mitsyanko static void 32598f44cb0SIgor Mitsyanko qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, 32698f44cb0SIgor Mitsyanko u16 frame_type, bool reg) 32798f44cb0SIgor Mitsyanko { 32898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 32998f44cb0SIgor Mitsyanko u16 mgmt_type; 33098f44cb0SIgor Mitsyanko u16 new_mask; 33198f44cb0SIgor Mitsyanko u16 qlink_frame_type = 0; 33298f44cb0SIgor Mitsyanko 33398f44cb0SIgor Mitsyanko mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; 33498f44cb0SIgor Mitsyanko 33598f44cb0SIgor Mitsyanko if (reg) 33698f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask | BIT(mgmt_type); 33798f44cb0SIgor Mitsyanko else 33898f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask & ~BIT(mgmt_type); 33998f44cb0SIgor Mitsyanko 34098f44cb0SIgor Mitsyanko if (new_mask == vif->mgmt_frames_bitmask) 34198f44cb0SIgor Mitsyanko return; 34298f44cb0SIgor Mitsyanko 34398f44cb0SIgor Mitsyanko switch (frame_type & IEEE80211_FCTL_STYPE) { 3443dd06cecSSergey Matyukevich case IEEE80211_STYPE_REASSOC_REQ: 3453dd06cecSSergey Matyukevich case IEEE80211_STYPE_ASSOC_REQ: 3463dd06cecSSergey Matyukevich qlink_frame_type = QLINK_MGMT_FRAME_ASSOC_REQ; 3473dd06cecSSergey Matyukevich break; 3483dd06cecSSergey Matyukevich case IEEE80211_STYPE_AUTH: 3493dd06cecSSergey Matyukevich qlink_frame_type = QLINK_MGMT_FRAME_AUTH; 3503dd06cecSSergey Matyukevich break; 35198f44cb0SIgor Mitsyanko case IEEE80211_STYPE_PROBE_REQ: 35298f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ; 35398f44cb0SIgor Mitsyanko break; 35498f44cb0SIgor Mitsyanko case IEEE80211_STYPE_ACTION: 35598f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_ACTION; 35698f44cb0SIgor Mitsyanko break; 35798f44cb0SIgor Mitsyanko default: 35898f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported frame type: %X\n", 35998f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, 36098f44cb0SIgor Mitsyanko (frame_type & IEEE80211_FCTL_STYPE) >> 4); 36198f44cb0SIgor Mitsyanko return; 36298f44cb0SIgor Mitsyanko } 36398f44cb0SIgor Mitsyanko 36498f44cb0SIgor Mitsyanko if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) { 36598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: failed to %sregister mgmt frame type 0x%x\n", 36698f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", 36798f44cb0SIgor Mitsyanko frame_type); 36898f44cb0SIgor Mitsyanko return; 36998f44cb0SIgor Mitsyanko } 37098f44cb0SIgor Mitsyanko 37198f44cb0SIgor Mitsyanko vif->mgmt_frames_bitmask = new_mask; 37298f44cb0SIgor Mitsyanko pr_debug("VIF%u.%u: %sregistered mgmt frame type 0x%x\n", 37398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", frame_type); 37498f44cb0SIgor Mitsyanko } 37598f44cb0SIgor Mitsyanko 37698f44cb0SIgor Mitsyanko static int 37798f44cb0SIgor Mitsyanko qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 37898f44cb0SIgor Mitsyanko struct cfg80211_mgmt_tx_params *params, u64 *cookie) 37998f44cb0SIgor Mitsyanko { 38098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 38198f44cb0SIgor Mitsyanko const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; 38298f44cb0SIgor Mitsyanko u32 short_cookie = prandom_u32(); 38398f44cb0SIgor Mitsyanko u16 flags = 0; 384e6e594afSIgor Mitsyanko u16 freq; 38598f44cb0SIgor Mitsyanko 38698f44cb0SIgor Mitsyanko *cookie = short_cookie; 38798f44cb0SIgor Mitsyanko 38898f44cb0SIgor Mitsyanko if (params->offchan) 38998f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN; 39098f44cb0SIgor Mitsyanko 39198f44cb0SIgor Mitsyanko if (params->no_cck) 39298f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_NO_CCK; 39398f44cb0SIgor Mitsyanko 39498f44cb0SIgor Mitsyanko if (params->dont_wait_for_ack) 39598f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT; 39698f44cb0SIgor Mitsyanko 397e6e594afSIgor Mitsyanko /* If channel is not specified, pass "freq = 0" to tell device 398e6e594afSIgor Mitsyanko * firmware to use current channel. 399e6e594afSIgor Mitsyanko */ 400e6e594afSIgor Mitsyanko if (params->chan) 401e6e594afSIgor Mitsyanko freq = params->chan->center_freq; 402e6e594afSIgor Mitsyanko else 403e6e594afSIgor Mitsyanko freq = 0; 404e6e594afSIgor Mitsyanko 40598f44cb0SIgor Mitsyanko pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n", 406e6e594afSIgor Mitsyanko wdev->netdev->name, freq, 40798f44cb0SIgor Mitsyanko le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da, 40898f44cb0SIgor Mitsyanko params->len, short_cookie, flags); 40998f44cb0SIgor Mitsyanko 41098f44cb0SIgor Mitsyanko return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags, 411e6e594afSIgor Mitsyanko freq, 41298f44cb0SIgor Mitsyanko params->buf, params->len); 41398f44cb0SIgor Mitsyanko } 41498f44cb0SIgor Mitsyanko 41598f44cb0SIgor Mitsyanko static int 41698f44cb0SIgor Mitsyanko qtnf_get_station(struct wiphy *wiphy, struct net_device *dev, 41798f44cb0SIgor Mitsyanko const u8 *mac, struct station_info *sinfo) 41898f44cb0SIgor Mitsyanko { 41998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 42098f44cb0SIgor Mitsyanko 4217a4d3a3bSIgor Mitsyanko sinfo->generation = vif->generation; 42298f44cb0SIgor Mitsyanko return qtnf_cmd_get_sta_info(vif, mac, sinfo); 42398f44cb0SIgor Mitsyanko } 42498f44cb0SIgor Mitsyanko 42598f44cb0SIgor Mitsyanko static int 42698f44cb0SIgor Mitsyanko qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev, 42798f44cb0SIgor Mitsyanko int idx, u8 *mac, struct station_info *sinfo) 42898f44cb0SIgor Mitsyanko { 42998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 43098f44cb0SIgor Mitsyanko const struct qtnf_sta_node *sta_node; 43198f44cb0SIgor Mitsyanko int ret; 43298f44cb0SIgor Mitsyanko 43398f44cb0SIgor Mitsyanko sta_node = qtnf_sta_list_lookup_index(&vif->sta_list, idx); 43498f44cb0SIgor Mitsyanko 43598f44cb0SIgor Mitsyanko if (unlikely(!sta_node)) 43698f44cb0SIgor Mitsyanko return -ENOENT; 43798f44cb0SIgor Mitsyanko 43898f44cb0SIgor Mitsyanko ether_addr_copy(mac, sta_node->mac_addr); 43998f44cb0SIgor Mitsyanko 44098f44cb0SIgor Mitsyanko ret = qtnf_cmd_get_sta_info(vif, sta_node->mac_addr, sinfo); 44198f44cb0SIgor Mitsyanko 44298f44cb0SIgor Mitsyanko if (unlikely(ret == -ENOENT)) { 4437a4d3a3bSIgor Mitsyanko qtnf_sta_list_del(vif, mac); 44498f44cb0SIgor Mitsyanko cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL); 44598f44cb0SIgor Mitsyanko sinfo->filled = 0; 44698f44cb0SIgor Mitsyanko } 44798f44cb0SIgor Mitsyanko 4487a4d3a3bSIgor Mitsyanko sinfo->generation = vif->generation; 4497a4d3a3bSIgor Mitsyanko 45098f44cb0SIgor Mitsyanko return ret; 45198f44cb0SIgor Mitsyanko } 45298f44cb0SIgor Mitsyanko 45398f44cb0SIgor Mitsyanko static int qtnf_add_key(struct wiphy *wiphy, struct net_device *dev, 45498f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr, 45598f44cb0SIgor Mitsyanko struct key_params *params) 45698f44cb0SIgor Mitsyanko { 45798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 45898f44cb0SIgor Mitsyanko int ret; 45998f44cb0SIgor Mitsyanko 46098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params); 46198f44cb0SIgor Mitsyanko if (ret) 46298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add key: cipher=%x idx=%u pw=%u\n", 46398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->cipher, key_index, 46498f44cb0SIgor Mitsyanko pairwise); 46598f44cb0SIgor Mitsyanko 46698f44cb0SIgor Mitsyanko return ret; 46798f44cb0SIgor Mitsyanko } 46898f44cb0SIgor Mitsyanko 46998f44cb0SIgor Mitsyanko static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev, 47098f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr) 47198f44cb0SIgor Mitsyanko { 47298f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 47398f44cb0SIgor Mitsyanko int ret; 47498f44cb0SIgor Mitsyanko 47598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr); 47698f44cb0SIgor Mitsyanko if (ret) 47798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n", 47898f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, pairwise); 47998f44cb0SIgor Mitsyanko 48098f44cb0SIgor Mitsyanko return ret; 48198f44cb0SIgor Mitsyanko } 48298f44cb0SIgor Mitsyanko 48398f44cb0SIgor Mitsyanko static int qtnf_set_default_key(struct wiphy *wiphy, struct net_device *dev, 48498f44cb0SIgor Mitsyanko u8 key_index, bool unicast, bool multicast) 48598f44cb0SIgor Mitsyanko { 48698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 48798f44cb0SIgor Mitsyanko int ret; 48898f44cb0SIgor Mitsyanko 48998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_key(vif, key_index, unicast, multicast); 49098f44cb0SIgor Mitsyanko if (ret) 49198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set dflt key: idx=%u uc=%u mc=%u\n", 49298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, unicast, 49398f44cb0SIgor Mitsyanko multicast); 49498f44cb0SIgor Mitsyanko 49598f44cb0SIgor Mitsyanko return ret; 49698f44cb0SIgor Mitsyanko } 49798f44cb0SIgor Mitsyanko 49898f44cb0SIgor Mitsyanko static int 49998f44cb0SIgor Mitsyanko qtnf_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, 50098f44cb0SIgor Mitsyanko u8 key_index) 50198f44cb0SIgor Mitsyanko { 50298f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 50398f44cb0SIgor Mitsyanko int ret; 50498f44cb0SIgor Mitsyanko 50598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_mgmt_key(vif, key_index); 50698f44cb0SIgor Mitsyanko if (ret) 50798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set default MGMT key: idx=%u\n", 50898f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index); 50998f44cb0SIgor Mitsyanko 51098f44cb0SIgor Mitsyanko return ret; 51198f44cb0SIgor Mitsyanko } 51298f44cb0SIgor Mitsyanko 51398f44cb0SIgor Mitsyanko static int 51498f44cb0SIgor Mitsyanko qtnf_change_station(struct wiphy *wiphy, struct net_device *dev, 51598f44cb0SIgor Mitsyanko const u8 *mac, struct station_parameters *params) 51698f44cb0SIgor Mitsyanko { 51798f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 51898f44cb0SIgor Mitsyanko int ret; 51998f44cb0SIgor Mitsyanko 52098f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_sta(vif, mac, params); 52198f44cb0SIgor Mitsyanko if (ret) 52298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change STA %pM\n", 52398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, mac); 52498f44cb0SIgor Mitsyanko 52598f44cb0SIgor Mitsyanko return ret; 52698f44cb0SIgor Mitsyanko } 52798f44cb0SIgor Mitsyanko 52898f44cb0SIgor Mitsyanko static int 52998f44cb0SIgor Mitsyanko qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, 53098f44cb0SIgor Mitsyanko struct station_del_parameters *params) 53198f44cb0SIgor Mitsyanko { 53298f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 53398f44cb0SIgor Mitsyanko int ret; 53498f44cb0SIgor Mitsyanko 53598f44cb0SIgor Mitsyanko if (params->mac && 53698f44cb0SIgor Mitsyanko (vif->wdev.iftype == NL80211_IFTYPE_AP) && 53798f44cb0SIgor Mitsyanko !is_broadcast_ether_addr(params->mac) && 53898f44cb0SIgor Mitsyanko !qtnf_sta_list_lookup(&vif->sta_list, params->mac)) 53998f44cb0SIgor Mitsyanko return 0; 54098f44cb0SIgor Mitsyanko 54198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_sta(vif, params); 54298f44cb0SIgor Mitsyanko if (ret) 54398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete STA %pM\n", 54498f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->mac); 54598f44cb0SIgor Mitsyanko return ret; 54698f44cb0SIgor Mitsyanko } 54798f44cb0SIgor Mitsyanko 5487e916cafSKees Cook static void qtnf_scan_timeout(struct timer_list *t) 549c7ead2abSSergey Matyukevich { 5507e916cafSKees Cook struct qtnf_wmac *mac = from_timer(mac, t, scan_timeout); 551c7ead2abSSergey Matyukevich 552c7ead2abSSergey Matyukevich pr_warn("mac%d scan timed out\n", mac->macid); 553c7ead2abSSergey Matyukevich qtnf_scan_done(mac, true); 554c7ead2abSSergey Matyukevich } 555c7ead2abSSergey Matyukevich 55698f44cb0SIgor Mitsyanko static int 55798f44cb0SIgor Mitsyanko qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) 55898f44cb0SIgor Mitsyanko { 55998f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 56098f44cb0SIgor Mitsyanko 56198f44cb0SIgor Mitsyanko mac->scan_req = request; 56298f44cb0SIgor Mitsyanko 563c7ead2abSSergey Matyukevich if (qtnf_cmd_send_scan(mac)) { 56498f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to start scan\n", mac->macid); 565c7ead2abSSergey Matyukevich mac->scan_req = NULL; 566c7ead2abSSergey Matyukevich return -EFAULT; 567c7ead2abSSergey Matyukevich } 56898f44cb0SIgor Mitsyanko 569841b86f3SKees Cook mac->scan_timeout.function = qtnf_scan_timeout; 570c7ead2abSSergey Matyukevich mod_timer(&mac->scan_timeout, 571c7ead2abSSergey Matyukevich jiffies + QTNF_SCAN_TIMEOUT_SEC * HZ); 572c7ead2abSSergey Matyukevich 573c7ead2abSSergey Matyukevich return 0; 57498f44cb0SIgor Mitsyanko } 57598f44cb0SIgor Mitsyanko 57698f44cb0SIgor Mitsyanko static int 57798f44cb0SIgor Mitsyanko qtnf_connect(struct wiphy *wiphy, struct net_device *dev, 57898f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 57998f44cb0SIgor Mitsyanko { 58098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 58198f44cb0SIgor Mitsyanko int ret; 58298f44cb0SIgor Mitsyanko 58398f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 58498f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 58598f44cb0SIgor Mitsyanko 58698f44cb0SIgor Mitsyanko if (vif->sta_state != QTNF_STA_DISCONNECTED) 58798f44cb0SIgor Mitsyanko return -EBUSY; 58898f44cb0SIgor Mitsyanko 58998f44cb0SIgor Mitsyanko if (sme->bssid) 5909766d1ddSIgor Mitsyanko ether_addr_copy(vif->bssid, sme->bssid); 59198f44cb0SIgor Mitsyanko else 5929766d1ddSIgor Mitsyanko eth_zero_addr(vif->bssid); 59398f44cb0SIgor Mitsyanko 59498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_connect(vif, sme); 59598f44cb0SIgor Mitsyanko if (ret) { 59698f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to connect\n", vif->mac->macid, 59798f44cb0SIgor Mitsyanko vif->vifid); 59898f44cb0SIgor Mitsyanko return ret; 59998f44cb0SIgor Mitsyanko } 60098f44cb0SIgor Mitsyanko 60198f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_CONNECTING; 60298f44cb0SIgor Mitsyanko return 0; 60398f44cb0SIgor Mitsyanko } 60498f44cb0SIgor Mitsyanko 60598f44cb0SIgor Mitsyanko static int 60698f44cb0SIgor Mitsyanko qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, 60798f44cb0SIgor Mitsyanko u16 reason_code) 60898f44cb0SIgor Mitsyanko { 60998f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 61098f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 61198f44cb0SIgor Mitsyanko int ret; 61298f44cb0SIgor Mitsyanko 61398f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 61498f44cb0SIgor Mitsyanko if (!vif) { 61598f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 61698f44cb0SIgor Mitsyanko return -EFAULT; 61798f44cb0SIgor Mitsyanko } 61898f44cb0SIgor Mitsyanko 61998f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 62098f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 62198f44cb0SIgor Mitsyanko 62298f44cb0SIgor Mitsyanko if (vif->sta_state == QTNF_STA_DISCONNECTED) 62398f44cb0SIgor Mitsyanko return 0; 62498f44cb0SIgor Mitsyanko 62598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_disconnect(vif, reason_code); 62698f44cb0SIgor Mitsyanko if (ret) { 62798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to disconnect\n", mac->macid, 62898f44cb0SIgor Mitsyanko vif->vifid); 62998f44cb0SIgor Mitsyanko return ret; 63098f44cb0SIgor Mitsyanko } 63198f44cb0SIgor Mitsyanko 63298f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 63398f44cb0SIgor Mitsyanko return 0; 63498f44cb0SIgor Mitsyanko } 63598f44cb0SIgor Mitsyanko 6367c04b439SSergey Matyukevich static int 6377c04b439SSergey Matyukevich qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, 6387c04b439SSergey Matyukevich int idx, struct survey_info *survey) 6397c04b439SSergey Matyukevich { 6407c04b439SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 641ef81e8e9SIgor Mitsyanko struct wireless_dev *wdev = dev->ieee80211_ptr; 6427c04b439SSergey Matyukevich struct ieee80211_supported_band *sband; 643ef81e8e9SIgor Mitsyanko const struct cfg80211_chan_def *chandef = &wdev->chandef; 6447c04b439SSergey Matyukevich struct ieee80211_channel *chan; 6457c04b439SSergey Matyukevich struct qtnf_chan_stats stats; 64627894448SSergey Matyukevich struct qtnf_vif *vif; 6477c04b439SSergey Matyukevich int ret; 6487c04b439SSergey Matyukevich 64927894448SSergey Matyukevich vif = qtnf_netdev_get_priv(dev); 65027894448SSergey Matyukevich 6517c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_2GHZ]; 6527c04b439SSergey Matyukevich if (sband && idx >= sband->n_channels) { 6537c04b439SSergey Matyukevich idx -= sband->n_channels; 6547c04b439SSergey Matyukevich sband = NULL; 6557c04b439SSergey Matyukevich } 6567c04b439SSergey Matyukevich 6577c04b439SSergey Matyukevich if (!sband) 6587c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_5GHZ]; 6597c04b439SSergey Matyukevich 6607c04b439SSergey Matyukevich if (!sband || idx >= sband->n_channels) 6617c04b439SSergey Matyukevich return -ENOENT; 6627c04b439SSergey Matyukevich 6637c04b439SSergey Matyukevich chan = &sband->channels[idx]; 6647c04b439SSergey Matyukevich memset(&stats, 0, sizeof(stats)); 6657c04b439SSergey Matyukevich 6667c04b439SSergey Matyukevich survey->channel = chan; 6677c04b439SSergey Matyukevich survey->filled = 0x0; 6687c04b439SSergey Matyukevich 66934f1145bSSergey Matyukevich if (chandef->chan) { 67034f1145bSSergey Matyukevich if (chan->hw_value == chandef->chan->hw_value) 67134f1145bSSergey Matyukevich survey->filled = SURVEY_INFO_IN_USE; 67234f1145bSSergey Matyukevich } 67327894448SSergey Matyukevich 6747c04b439SSergey Matyukevich ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); 6757c04b439SSergey Matyukevich switch (ret) { 6767c04b439SSergey Matyukevich case 0: 6777c04b439SSergey Matyukevich if (unlikely(stats.chan_num != chan->hw_value)) { 6787c04b439SSergey Matyukevich pr_err("received stats for channel %d instead of %d\n", 6797c04b439SSergey Matyukevich stats.chan_num, chan->hw_value); 6807c04b439SSergey Matyukevich ret = -EINVAL; 6817c04b439SSergey Matyukevich break; 6827c04b439SSergey Matyukevich } 6837c04b439SSergey Matyukevich 68434f1145bSSergey Matyukevich survey->filled |= SURVEY_INFO_TIME | 6857c04b439SSergey Matyukevich SURVEY_INFO_TIME_SCAN | 6867c04b439SSergey Matyukevich SURVEY_INFO_TIME_BUSY | 6877c04b439SSergey Matyukevich SURVEY_INFO_TIME_RX | 6887c04b439SSergey Matyukevich SURVEY_INFO_TIME_TX | 6897c04b439SSergey Matyukevich SURVEY_INFO_NOISE_DBM; 6907c04b439SSergey Matyukevich 6917c04b439SSergey Matyukevich survey->time_scan = stats.cca_try; 6927c04b439SSergey Matyukevich survey->time = stats.cca_try; 6937c04b439SSergey Matyukevich survey->time_tx = stats.cca_tx; 6947c04b439SSergey Matyukevich survey->time_rx = stats.cca_rx; 6957c04b439SSergey Matyukevich survey->time_busy = stats.cca_busy; 6967c04b439SSergey Matyukevich survey->noise = stats.chan_noise; 6977c04b439SSergey Matyukevich break; 6987c04b439SSergey Matyukevich case -ENOENT: 6997c04b439SSergey Matyukevich pr_debug("no stats for channel %u\n", chan->hw_value); 7007c04b439SSergey Matyukevich ret = 0; 7017c04b439SSergey Matyukevich break; 7027c04b439SSergey Matyukevich default: 7037c04b439SSergey Matyukevich pr_debug("failed to get chan(%d) stats from card\n", 7047c04b439SSergey Matyukevich chan->hw_value); 7057c04b439SSergey Matyukevich ret = -EINVAL; 7067c04b439SSergey Matyukevich break; 7077c04b439SSergey Matyukevich } 7087c04b439SSergey Matyukevich 7097c04b439SSergey Matyukevich return ret; 7107c04b439SSergey Matyukevich } 7117c04b439SSergey Matyukevich 71227894448SSergey Matyukevich static int 71327894448SSergey Matyukevich qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, 71427894448SSergey Matyukevich struct cfg80211_chan_def *chandef) 71527894448SSergey Matyukevich { 71627894448SSergey Matyukevich struct net_device *ndev = wdev->netdev; 71727894448SSergey Matyukevich struct qtnf_vif *vif; 7189e5478b6SIgor Mitsyanko int ret; 71927894448SSergey Matyukevich 72027894448SSergey Matyukevich if (!ndev) 72127894448SSergey Matyukevich return -ENODEV; 72227894448SSergey Matyukevich 72327894448SSergey Matyukevich vif = qtnf_netdev_get_priv(wdev->netdev); 72427894448SSergey Matyukevich 7259e5478b6SIgor Mitsyanko ret = qtnf_cmd_get_channel(vif, chandef); 7269e5478b6SIgor Mitsyanko if (ret) { 7279e5478b6SIgor Mitsyanko pr_err("%s: failed to get channel: %d\n", ndev->name, ret); 7289e5478b6SIgor Mitsyanko goto out; 72927894448SSergey Matyukevich } 73027894448SSergey Matyukevich 7319e5478b6SIgor Mitsyanko if (!cfg80211_chandef_valid(chandef)) { 7325bf374abSSergey Matyukevich pr_err("%s: bad channel freq=%u cf1=%u cf2=%u bw=%u\n", 7335bf374abSSergey Matyukevich ndev->name, chandef->chan->center_freq, 7349e5478b6SIgor Mitsyanko chandef->center_freq1, chandef->center_freq2, 7359e5478b6SIgor Mitsyanko chandef->width); 7369e5478b6SIgor Mitsyanko ret = -ENODATA; 73734f1145bSSergey Matyukevich } 73834f1145bSSergey Matyukevich 7399e5478b6SIgor Mitsyanko out: 7409e5478b6SIgor Mitsyanko return ret; 74127894448SSergey Matyukevich } 74227894448SSergey Matyukevich 74397883695SSergey Matyukevich static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, 74497883695SSergey Matyukevich struct cfg80211_csa_settings *params) 74597883695SSergey Matyukevich { 74697883695SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 74797883695SSergey Matyukevich int ret; 74897883695SSergey Matyukevich 74997883695SSergey Matyukevich pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, 75097883695SSergey Matyukevich params->chandef.chan->hw_value, params->count, 75197883695SSergey Matyukevich params->radar_required, params->block_tx); 75297883695SSergey Matyukevich 75397883695SSergey Matyukevich if (!cfg80211_chandef_valid(¶ms->chandef)) { 75497883695SSergey Matyukevich pr_err("%s: invalid channel\n", dev->name); 75597883695SSergey Matyukevich return -EINVAL; 75697883695SSergey Matyukevich } 75797883695SSergey Matyukevich 7588c015b90SIgor Mitsyanko ret = qtnf_cmd_send_chan_switch(vif, params); 75997883695SSergey Matyukevich if (ret) 76097883695SSergey Matyukevich pr_warn("%s: failed to switch to channel (%u)\n", 76197883695SSergey Matyukevich dev->name, params->chandef.chan->hw_value); 76297883695SSergey Matyukevich 76397883695SSergey Matyukevich return ret; 76497883695SSergey Matyukevich } 76597883695SSergey Matyukevich 766b05ee456SIgor Mitsyanko static int qtnf_start_radar_detection(struct wiphy *wiphy, 767b05ee456SIgor Mitsyanko struct net_device *ndev, 768b05ee456SIgor Mitsyanko struct cfg80211_chan_def *chandef, 769b05ee456SIgor Mitsyanko u32 cac_time_ms) 770b05ee456SIgor Mitsyanko { 771b05ee456SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 772b05ee456SIgor Mitsyanko int ret; 773b05ee456SIgor Mitsyanko 774b05ee456SIgor Mitsyanko ret = qtnf_cmd_start_cac(vif, chandef, cac_time_ms); 775b05ee456SIgor Mitsyanko if (ret) 776b05ee456SIgor Mitsyanko pr_err("%s: failed to start CAC ret=%d\n", ndev->name, ret); 777b05ee456SIgor Mitsyanko 778b05ee456SIgor Mitsyanko return ret; 779b05ee456SIgor Mitsyanko } 780b05ee456SIgor Mitsyanko 781f1398fd2SVasily Ulyanov static int qtnf_set_mac_acl(struct wiphy *wiphy, 782f1398fd2SVasily Ulyanov struct net_device *dev, 783f1398fd2SVasily Ulyanov const struct cfg80211_acl_data *params) 784f1398fd2SVasily Ulyanov { 785f1398fd2SVasily Ulyanov struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 786f1398fd2SVasily Ulyanov int ret; 787f1398fd2SVasily Ulyanov 788f1398fd2SVasily Ulyanov ret = qtnf_cmd_set_mac_acl(vif, params); 789f1398fd2SVasily Ulyanov if (ret) 790f1398fd2SVasily Ulyanov pr_err("%s: failed to set mac ACL ret=%d\n", dev->name, ret); 791f1398fd2SVasily Ulyanov 792f1398fd2SVasily Ulyanov return ret; 793f1398fd2SVasily Ulyanov } 794f1398fd2SVasily Ulyanov 79598f44cb0SIgor Mitsyanko static struct cfg80211_ops qtn_cfg80211_ops = { 79698f44cb0SIgor Mitsyanko .add_virtual_intf = qtnf_add_virtual_intf, 79798f44cb0SIgor Mitsyanko .change_virtual_intf = qtnf_change_virtual_intf, 79898f44cb0SIgor Mitsyanko .del_virtual_intf = qtnf_del_virtual_intf, 79998f44cb0SIgor Mitsyanko .start_ap = qtnf_start_ap, 80098f44cb0SIgor Mitsyanko .change_beacon = qtnf_change_beacon, 80198f44cb0SIgor Mitsyanko .stop_ap = qtnf_stop_ap, 80298f44cb0SIgor Mitsyanko .set_wiphy_params = qtnf_set_wiphy_params, 80398f44cb0SIgor Mitsyanko .mgmt_frame_register = qtnf_mgmt_frame_register, 80498f44cb0SIgor Mitsyanko .mgmt_tx = qtnf_mgmt_tx, 80598f44cb0SIgor Mitsyanko .change_station = qtnf_change_station, 80698f44cb0SIgor Mitsyanko .del_station = qtnf_del_station, 80798f44cb0SIgor Mitsyanko .get_station = qtnf_get_station, 80898f44cb0SIgor Mitsyanko .dump_station = qtnf_dump_station, 80998f44cb0SIgor Mitsyanko .add_key = qtnf_add_key, 81098f44cb0SIgor Mitsyanko .del_key = qtnf_del_key, 81198f44cb0SIgor Mitsyanko .set_default_key = qtnf_set_default_key, 81298f44cb0SIgor Mitsyanko .set_default_mgmt_key = qtnf_set_default_mgmt_key, 81398f44cb0SIgor Mitsyanko .scan = qtnf_scan, 81498f44cb0SIgor Mitsyanko .connect = qtnf_connect, 8157c04b439SSergey Matyukevich .disconnect = qtnf_disconnect, 81627894448SSergey Matyukevich .dump_survey = qtnf_dump_survey, 81797883695SSergey Matyukevich .get_channel = qtnf_get_channel, 818b05ee456SIgor Mitsyanko .channel_switch = qtnf_channel_switch, 819b05ee456SIgor Mitsyanko .start_radar_detection = qtnf_start_radar_detection, 820f1398fd2SVasily Ulyanov .set_mac_acl = qtnf_set_mac_acl, 82198f44cb0SIgor Mitsyanko }; 82298f44cb0SIgor Mitsyanko 8234dd07d2bSSergey Matyukevich static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, 82498f44cb0SIgor Mitsyanko struct regulatory_request *req) 82598f44cb0SIgor Mitsyanko { 8264dd07d2bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy_in); 8274dd07d2bSSergey Matyukevich struct qtnf_bus *bus = mac->bus; 8284dd07d2bSSergey Matyukevich struct wiphy *wiphy; 8294dd07d2bSSergey Matyukevich unsigned int mac_idx; 83098f44cb0SIgor Mitsyanko enum nl80211_band band; 8314dd07d2bSSergey Matyukevich int ret; 83298f44cb0SIgor Mitsyanko 83398f44cb0SIgor Mitsyanko pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, 83498f44cb0SIgor Mitsyanko req->alpha2[0], req->alpha2[1]); 83598f44cb0SIgor Mitsyanko 8364dd07d2bSSergey Matyukevich ret = qtnf_cmd_reg_notify(bus, req); 8374dd07d2bSSergey Matyukevich if (ret) { 8384dd07d2bSSergey Matyukevich if (ret != -EOPNOTSUPP && ret != -EALREADY) 8394dd07d2bSSergey Matyukevich pr_err("failed to update reg domain to %c%c\n", 8404dd07d2bSSergey Matyukevich req->alpha2[0], req->alpha2[1]); 84198f44cb0SIgor Mitsyanko return; 84298f44cb0SIgor Mitsyanko } 84398f44cb0SIgor Mitsyanko 8444dd07d2bSSergey Matyukevich for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { 8454dd07d2bSSergey Matyukevich if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) 84698f44cb0SIgor Mitsyanko continue; 84798f44cb0SIgor Mitsyanko 8484dd07d2bSSergey Matyukevich mac = bus->mac[mac_idx]; 8494fd045cdSIgor Mitsyanko if (!mac) 8504fd045cdSIgor Mitsyanko continue; 8514fd045cdSIgor Mitsyanko 8524dd07d2bSSergey Matyukevich wiphy = priv_to_wiphy(mac); 85398f44cb0SIgor Mitsyanko 85498f44cb0SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; ++band) { 85598f44cb0SIgor Mitsyanko if (!wiphy->bands[band]) 85698f44cb0SIgor Mitsyanko continue; 85798f44cb0SIgor Mitsyanko 858e294cbfdSIgor Mitsyanko ret = qtnf_cmd_band_info_get(mac, wiphy->bands[band]); 8594dd07d2bSSergey Matyukevich if (ret) 8604dd07d2bSSergey Matyukevich pr_err("failed to get chan info for mac %u band %u\n", 8614dd07d2bSSergey Matyukevich mac_idx, band); 86298f44cb0SIgor Mitsyanko } 86398f44cb0SIgor Mitsyanko } 86498f44cb0SIgor Mitsyanko } 86598f44cb0SIgor Mitsyanko 86698f44cb0SIgor Mitsyanko struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) 86798f44cb0SIgor Mitsyanko { 86898f44cb0SIgor Mitsyanko struct wiphy *wiphy; 86998f44cb0SIgor Mitsyanko 87098f44cb0SIgor Mitsyanko wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac)); 87198f44cb0SIgor Mitsyanko if (!wiphy) 87298f44cb0SIgor Mitsyanko return NULL; 87398f44cb0SIgor Mitsyanko 87498f44cb0SIgor Mitsyanko set_wiphy_dev(wiphy, bus->dev); 87598f44cb0SIgor Mitsyanko 87698f44cb0SIgor Mitsyanko return wiphy; 87798f44cb0SIgor Mitsyanko } 87898f44cb0SIgor Mitsyanko 87998f44cb0SIgor Mitsyanko static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, 88098f44cb0SIgor Mitsyanko struct ieee80211_iface_combination *if_comb, 88198f44cb0SIgor Mitsyanko const struct qtnf_mac_info *mac_info) 88298f44cb0SIgor Mitsyanko { 88398f44cb0SIgor Mitsyanko size_t max_interfaces = 0; 88498f44cb0SIgor Mitsyanko u16 interface_modes = 0; 88598f44cb0SIgor Mitsyanko size_t i; 88698f44cb0SIgor Mitsyanko 88798f44cb0SIgor Mitsyanko if (unlikely(!mac_info->limits || !mac_info->n_limits)) 88898f44cb0SIgor Mitsyanko return -ENOENT; 88998f44cb0SIgor Mitsyanko 89098f44cb0SIgor Mitsyanko if_comb->limits = mac_info->limits; 89198f44cb0SIgor Mitsyanko if_comb->n_limits = mac_info->n_limits; 89298f44cb0SIgor Mitsyanko 89398f44cb0SIgor Mitsyanko for (i = 0; i < mac_info->n_limits; i++) { 89498f44cb0SIgor Mitsyanko max_interfaces += mac_info->limits[i].max; 89598f44cb0SIgor Mitsyanko interface_modes |= mac_info->limits[i].types; 89698f44cb0SIgor Mitsyanko } 89798f44cb0SIgor Mitsyanko 89898f44cb0SIgor Mitsyanko if_comb->num_different_channels = 1; 89998f44cb0SIgor Mitsyanko if_comb->beacon_int_infra_match = true; 90098f44cb0SIgor Mitsyanko if_comb->max_interfaces = max_interfaces; 90198f44cb0SIgor Mitsyanko if_comb->radar_detect_widths = mac_info->radar_detect_widths; 90298f44cb0SIgor Mitsyanko wiphy->interface_modes = interface_modes; 90398f44cb0SIgor Mitsyanko 90498f44cb0SIgor Mitsyanko return 0; 90598f44cb0SIgor Mitsyanko } 90698f44cb0SIgor Mitsyanko 90798f44cb0SIgor Mitsyanko int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) 90898f44cb0SIgor Mitsyanko { 90998f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 91098f44cb0SIgor Mitsyanko struct ieee80211_iface_combination *iface_comb = NULL; 91198f44cb0SIgor Mitsyanko int ret; 91298f44cb0SIgor Mitsyanko 91398f44cb0SIgor Mitsyanko if (!wiphy) { 91498f44cb0SIgor Mitsyanko pr_err("invalid wiphy pointer\n"); 91598f44cb0SIgor Mitsyanko return -EFAULT; 91698f44cb0SIgor Mitsyanko } 91798f44cb0SIgor Mitsyanko 91898f44cb0SIgor Mitsyanko iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); 9194dd07d2bSSergey Matyukevich if (!iface_comb) 9204dd07d2bSSergey Matyukevich return -ENOMEM; 92198f44cb0SIgor Mitsyanko 92298f44cb0SIgor Mitsyanko ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); 92398f44cb0SIgor Mitsyanko if (ret) 92498f44cb0SIgor Mitsyanko goto out; 92598f44cb0SIgor Mitsyanko 92698f44cb0SIgor Mitsyanko wiphy->frag_threshold = mac->macinfo.frag_thr; 92798f44cb0SIgor Mitsyanko wiphy->rts_threshold = mac->macinfo.rts_thr; 92898f44cb0SIgor Mitsyanko wiphy->retry_short = mac->macinfo.sretry_limit; 92998f44cb0SIgor Mitsyanko wiphy->retry_long = mac->macinfo.lretry_limit; 93098f44cb0SIgor Mitsyanko wiphy->coverage_class = mac->macinfo.coverage_class; 93198f44cb0SIgor Mitsyanko 93298f44cb0SIgor Mitsyanko wiphy->max_scan_ssids = QTNF_MAX_SSID_LIST_LENGTH; 93398f44cb0SIgor Mitsyanko wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; 93498f44cb0SIgor Mitsyanko wiphy->mgmt_stypes = qtnf_mgmt_stypes; 93598f44cb0SIgor Mitsyanko wiphy->max_remain_on_channel_duration = 5000; 936f1398fd2SVasily Ulyanov wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs; 93798f44cb0SIgor Mitsyanko 93898f44cb0SIgor Mitsyanko wiphy->iface_combinations = iface_comb; 93998f44cb0SIgor Mitsyanko wiphy->n_iface_combinations = 1; 94097883695SSergey Matyukevich wiphy->max_num_csa_counters = 2; 94198f44cb0SIgor Mitsyanko 94298f44cb0SIgor Mitsyanko /* Initialize cipher suits */ 94398f44cb0SIgor Mitsyanko wiphy->cipher_suites = qtnf_cipher_suites; 94498f44cb0SIgor Mitsyanko wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); 94598f44cb0SIgor Mitsyanko wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 94698f44cb0SIgor Mitsyanko wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | 94798f44cb0SIgor Mitsyanko WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 94897883695SSergey Matyukevich WIPHY_FLAG_AP_UAPSD | 94997883695SSergey Matyukevich WIPHY_FLAG_HAS_CHANNEL_SWITCH; 95098f44cb0SIgor Mitsyanko 95198f44cb0SIgor Mitsyanko wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 95298f44cb0SIgor Mitsyanko NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; 95398f44cb0SIgor Mitsyanko 95498f44cb0SIgor Mitsyanko wiphy->available_antennas_tx = mac->macinfo.num_tx_chain; 95598f44cb0SIgor Mitsyanko wiphy->available_antennas_rx = mac->macinfo.num_rx_chain; 95698f44cb0SIgor Mitsyanko 95798f44cb0SIgor Mitsyanko wiphy->max_ap_assoc_sta = mac->macinfo.max_ap_assoc_sta; 958d42df85fSIgor Mitsyanko wiphy->ht_capa_mod_mask = &mac->macinfo.ht_cap_mod_mask; 959d42df85fSIgor Mitsyanko wiphy->vht_capa_mod_mask = &mac->macinfo.vht_cap_mod_mask; 96098f44cb0SIgor Mitsyanko 96198f44cb0SIgor Mitsyanko ether_addr_copy(wiphy->perm_addr, mac->macaddr); 96298f44cb0SIgor Mitsyanko 963db5c6d4aSIgor Mitsyanko if (hw_info->hw_capab & QLINK_HW_CAPAB_STA_INACT_TIMEOUT) 964db5c6d4aSIgor Mitsyanko wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; 965db5c6d4aSIgor Mitsyanko 966db5c6d4aSIgor Mitsyanko if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) { 9674dd07d2bSSergey Matyukevich wiphy->regulatory_flags |= REGULATORY_STRICT_REG | 9684dd07d2bSSergey Matyukevich REGULATORY_CUSTOM_REG; 96998f44cb0SIgor Mitsyanko wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; 9704dd07d2bSSergey Matyukevich wiphy_apply_custom_regulatory(wiphy, hw_info->rd); 97198f44cb0SIgor Mitsyanko } else { 97298f44cb0SIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; 97398f44cb0SIgor Mitsyanko } 97498f44cb0SIgor Mitsyanko 9750b419d01SVasily Ulyanov strlcpy(wiphy->fw_version, hw_info->fw_version, 9760b419d01SVasily Ulyanov sizeof(wiphy->fw_version)); 9770b419d01SVasily Ulyanov wiphy->hw_version = hw_info->hw_version; 9780b419d01SVasily Ulyanov 97998f44cb0SIgor Mitsyanko ret = wiphy_register(wiphy); 980ea19479fSSergey Matyukevich if (ret < 0) 981ea19479fSSergey Matyukevich goto out; 982ea19479fSSergey Matyukevich 983ea19479fSSergey Matyukevich if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) 984ea19479fSSergey Matyukevich ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); 985ea19479fSSergey Matyukevich else if (isalpha(hw_info->rd->alpha2[0]) && 986ea19479fSSergey Matyukevich isalpha(hw_info->rd->alpha2[1])) 987ea19479fSSergey Matyukevich ret = regulatory_hint(wiphy, hw_info->rd->alpha2); 988ea19479fSSergey Matyukevich 98998f44cb0SIgor Mitsyanko out: 9904dd07d2bSSergey Matyukevich if (ret) { 99198f44cb0SIgor Mitsyanko kfree(iface_comb); 99298f44cb0SIgor Mitsyanko return ret; 99398f44cb0SIgor Mitsyanko } 99498f44cb0SIgor Mitsyanko 99598f44cb0SIgor Mitsyanko return 0; 99698f44cb0SIgor Mitsyanko } 99798f44cb0SIgor Mitsyanko 99898f44cb0SIgor Mitsyanko void qtnf_netdev_updown(struct net_device *ndev, bool up) 99998f44cb0SIgor Mitsyanko { 100098f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 100198f44cb0SIgor Mitsyanko 100298f44cb0SIgor Mitsyanko if (qtnf_cmd_send_updown_intf(vif, up)) 100398f44cb0SIgor Mitsyanko pr_err("failed to send up/down command to FW\n"); 100498f44cb0SIgor Mitsyanko } 100598f44cb0SIgor Mitsyanko 100698f44cb0SIgor Mitsyanko void qtnf_virtual_intf_cleanup(struct net_device *ndev) 100798f44cb0SIgor Mitsyanko { 100898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 10094f8e2545SColin Ian King struct qtnf_wmac *mac = wiphy_priv(vif->wdev.wiphy); 101098f44cb0SIgor Mitsyanko 101198f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 101298f44cb0SIgor Mitsyanko switch (vif->sta_state) { 101398f44cb0SIgor Mitsyanko case QTNF_STA_DISCONNECTED: 101498f44cb0SIgor Mitsyanko break; 101598f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTING: 101698f44cb0SIgor Mitsyanko cfg80211_connect_result(vif->netdev, 10179766d1ddSIgor Mitsyanko vif->bssid, NULL, 0, 101898f44cb0SIgor Mitsyanko NULL, 0, 101998f44cb0SIgor Mitsyanko WLAN_STATUS_UNSPECIFIED_FAILURE, 102098f44cb0SIgor Mitsyanko GFP_KERNEL); 102198f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 102298f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 102398f44cb0SIgor Mitsyanko break; 102498f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTED: 102598f44cb0SIgor Mitsyanko cfg80211_disconnected(vif->netdev, 102698f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING, 102798f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 102898f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 102998f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 103098f44cb0SIgor Mitsyanko break; 103198f44cb0SIgor Mitsyanko } 103298f44cb0SIgor Mitsyanko 103398f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 103498f44cb0SIgor Mitsyanko } 1035a715b3a0SSergey Matyukevich 1036a715b3a0SSergey Matyukevich qtnf_scan_done(mac, true); 103798f44cb0SIgor Mitsyanko } 103898f44cb0SIgor Mitsyanko 103998f44cb0SIgor Mitsyanko void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) 104098f44cb0SIgor Mitsyanko { 104198f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 104298f44cb0SIgor Mitsyanko switch (vif->sta_state) { 104398f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTING: 104498f44cb0SIgor Mitsyanko cfg80211_connect_result(vif->netdev, 10459766d1ddSIgor Mitsyanko vif->bssid, NULL, 0, 104698f44cb0SIgor Mitsyanko NULL, 0, 104798f44cb0SIgor Mitsyanko WLAN_STATUS_UNSPECIFIED_FAILURE, 104898f44cb0SIgor Mitsyanko GFP_KERNEL); 104998f44cb0SIgor Mitsyanko break; 105098f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTED: 105198f44cb0SIgor Mitsyanko cfg80211_disconnected(vif->netdev, 105298f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING, 105398f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 105498f44cb0SIgor Mitsyanko break; 105598f44cb0SIgor Mitsyanko case QTNF_STA_DISCONNECTED: 105698f44cb0SIgor Mitsyanko break; 105798f44cb0SIgor Mitsyanko } 105898f44cb0SIgor Mitsyanko } 105998f44cb0SIgor Mitsyanko 106098f44cb0SIgor Mitsyanko cfg80211_shutdown_all_interfaces(vif->wdev.wiphy); 106198f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 106298f44cb0SIgor Mitsyanko } 106398f44cb0SIgor Mitsyanko 106498f44cb0SIgor Mitsyanko void qtnf_band_init_rates(struct ieee80211_supported_band *band) 106598f44cb0SIgor Mitsyanko { 106698f44cb0SIgor Mitsyanko switch (band->band) { 106798f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 106898f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_2g; 106998f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_2g); 107098f44cb0SIgor Mitsyanko break; 107198f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 107298f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_5g; 107398f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_5g); 107498f44cb0SIgor Mitsyanko break; 107598f44cb0SIgor Mitsyanko default: 107698f44cb0SIgor Mitsyanko band->bitrates = NULL; 107798f44cb0SIgor Mitsyanko band->n_bitrates = 0; 107898f44cb0SIgor Mitsyanko break; 107998f44cb0SIgor Mitsyanko } 108098f44cb0SIgor Mitsyanko } 1081