198f44cb0SIgor Mitsyanko /* 298f44cb0SIgor Mitsyanko * Copyright (c) 2012-2012 Quantenna Communications, Inc. 398f44cb0SIgor Mitsyanko * All rights reserved. 498f44cb0SIgor Mitsyanko * 598f44cb0SIgor Mitsyanko * This program is free software; you can redistribute it and/or 698f44cb0SIgor Mitsyanko * modify it under the terms of the GNU General Public License 798f44cb0SIgor Mitsyanko * as published by the Free Software Foundation; either version 2 898f44cb0SIgor Mitsyanko * of the License, or (at your option) any later version. 998f44cb0SIgor Mitsyanko * 1098f44cb0SIgor Mitsyanko * This program is distributed in the hope that it will be useful, 1198f44cb0SIgor Mitsyanko * but WITHOUT ANY WARRANTY; without even the implied warranty of 1298f44cb0SIgor Mitsyanko * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1398f44cb0SIgor Mitsyanko * GNU General Public License for more details. 1498f44cb0SIgor Mitsyanko * 1598f44cb0SIgor Mitsyanko */ 1698f44cb0SIgor Mitsyanko 1798f44cb0SIgor Mitsyanko #include <linux/kernel.h> 1898f44cb0SIgor Mitsyanko #include <linux/etherdevice.h> 1998f44cb0SIgor Mitsyanko #include <linux/vmalloc.h> 2098f44cb0SIgor Mitsyanko #include <linux/ieee80211.h> 2198f44cb0SIgor Mitsyanko #include <net/cfg80211.h> 2298f44cb0SIgor Mitsyanko #include <net/netlink.h> 2398f44cb0SIgor Mitsyanko 2498f44cb0SIgor Mitsyanko #include "cfg80211.h" 2598f44cb0SIgor Mitsyanko #include "commands.h" 2698f44cb0SIgor Mitsyanko #include "core.h" 2798f44cb0SIgor Mitsyanko #include "util.h" 2898f44cb0SIgor Mitsyanko #include "bus.h" 2998f44cb0SIgor Mitsyanko 3098f44cb0SIgor Mitsyanko /* Supported rates to be advertised to the cfg80211 */ 3198f44cb0SIgor Mitsyanko static struct ieee80211_rate qtnf_rates_2g[] = { 3298f44cb0SIgor Mitsyanko {.bitrate = 10, .hw_value = 2, }, 3398f44cb0SIgor Mitsyanko {.bitrate = 20, .hw_value = 4, }, 3498f44cb0SIgor Mitsyanko {.bitrate = 55, .hw_value = 11, }, 3598f44cb0SIgor Mitsyanko {.bitrate = 110, .hw_value = 22, }, 3698f44cb0SIgor Mitsyanko {.bitrate = 60, .hw_value = 12, }, 3798f44cb0SIgor Mitsyanko {.bitrate = 90, .hw_value = 18, }, 3898f44cb0SIgor Mitsyanko {.bitrate = 120, .hw_value = 24, }, 3998f44cb0SIgor Mitsyanko {.bitrate = 180, .hw_value = 36, }, 4098f44cb0SIgor Mitsyanko {.bitrate = 240, .hw_value = 48, }, 4198f44cb0SIgor Mitsyanko {.bitrate = 360, .hw_value = 72, }, 4298f44cb0SIgor Mitsyanko {.bitrate = 480, .hw_value = 96, }, 4398f44cb0SIgor Mitsyanko {.bitrate = 540, .hw_value = 108, }, 4498f44cb0SIgor Mitsyanko }; 4598f44cb0SIgor Mitsyanko 4698f44cb0SIgor Mitsyanko /* Supported rates to be advertised to the cfg80211 */ 4798f44cb0SIgor Mitsyanko static struct ieee80211_rate qtnf_rates_5g[] = { 4898f44cb0SIgor Mitsyanko {.bitrate = 60, .hw_value = 12, }, 4998f44cb0SIgor Mitsyanko {.bitrate = 90, .hw_value = 18, }, 5098f44cb0SIgor Mitsyanko {.bitrate = 120, .hw_value = 24, }, 5198f44cb0SIgor Mitsyanko {.bitrate = 180, .hw_value = 36, }, 5298f44cb0SIgor Mitsyanko {.bitrate = 240, .hw_value = 48, }, 5398f44cb0SIgor Mitsyanko {.bitrate = 360, .hw_value = 72, }, 5498f44cb0SIgor Mitsyanko {.bitrate = 480, .hw_value = 96, }, 5598f44cb0SIgor Mitsyanko {.bitrate = 540, .hw_value = 108, }, 5698f44cb0SIgor Mitsyanko }; 5798f44cb0SIgor Mitsyanko 5898f44cb0SIgor Mitsyanko /* Supported crypto cipher suits to be advertised to cfg80211 */ 5998f44cb0SIgor Mitsyanko static const u32 qtnf_cipher_suites[] = { 6098f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_TKIP, 6198f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_CCMP, 6298f44cb0SIgor Mitsyanko WLAN_CIPHER_SUITE_AES_CMAC, 6398f44cb0SIgor Mitsyanko }; 6498f44cb0SIgor Mitsyanko 6598f44cb0SIgor Mitsyanko /* Supported mgmt frame types to be advertised to cfg80211 */ 6698f44cb0SIgor Mitsyanko static const struct ieee80211_txrx_stypes 6798f44cb0SIgor Mitsyanko qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = { 6898f44cb0SIgor Mitsyanko [NL80211_IFTYPE_STATION] = { 6998f44cb0SIgor Mitsyanko .tx = BIT(IEEE80211_STYPE_ACTION >> 4), 7098f44cb0SIgor Mitsyanko .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 7198f44cb0SIgor Mitsyanko BIT(IEEE80211_STYPE_PROBE_REQ >> 4), 7298f44cb0SIgor Mitsyanko }, 7398f44cb0SIgor Mitsyanko [NL80211_IFTYPE_AP] = { 7498f44cb0SIgor Mitsyanko .tx = BIT(IEEE80211_STYPE_ACTION >> 4), 7598f44cb0SIgor Mitsyanko .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 7698f44cb0SIgor Mitsyanko BIT(IEEE80211_STYPE_PROBE_REQ >> 4), 7798f44cb0SIgor Mitsyanko }, 7898f44cb0SIgor Mitsyanko }; 7998f44cb0SIgor Mitsyanko 8098f44cb0SIgor Mitsyanko static int 8198f44cb0SIgor Mitsyanko qtnf_change_virtual_intf(struct wiphy *wiphy, 8298f44cb0SIgor Mitsyanko struct net_device *dev, 8398f44cb0SIgor Mitsyanko enum nl80211_iftype type, 8498f44cb0SIgor Mitsyanko struct vif_params *params) 8598f44cb0SIgor Mitsyanko { 8698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 8798f44cb0SIgor Mitsyanko u8 *mac_addr; 8898f44cb0SIgor Mitsyanko int ret; 8998f44cb0SIgor Mitsyanko 9098f44cb0SIgor Mitsyanko if (params) 9198f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 9298f44cb0SIgor Mitsyanko else 9398f44cb0SIgor Mitsyanko mac_addr = NULL; 9498f44cb0SIgor Mitsyanko 9598f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 9698f44cb0SIgor Mitsyanko 9798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_intf_type(vif, type, mac_addr); 9898f44cb0SIgor Mitsyanko if (ret) { 9998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change VIF type: %d\n", 10098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, ret); 10198f44cb0SIgor Mitsyanko return ret; 10298f44cb0SIgor Mitsyanko } 10398f44cb0SIgor Mitsyanko 10498f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 10598f44cb0SIgor Mitsyanko return 0; 10698f44cb0SIgor Mitsyanko } 10798f44cb0SIgor Mitsyanko 10898f44cb0SIgor Mitsyanko int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) 10998f44cb0SIgor Mitsyanko { 11098f44cb0SIgor Mitsyanko struct net_device *netdev = wdev->netdev; 11198f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 11298f44cb0SIgor Mitsyanko 11398f44cb0SIgor Mitsyanko if (WARN_ON(!netdev)) 11498f44cb0SIgor Mitsyanko return -EFAULT; 11598f44cb0SIgor Mitsyanko 11698f44cb0SIgor Mitsyanko vif = qtnf_netdev_get_priv(wdev->netdev); 11798f44cb0SIgor Mitsyanko 11898f44cb0SIgor Mitsyanko if (qtnf_cmd_send_del_intf(vif)) 11998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, 12098f44cb0SIgor Mitsyanko vif->vifid); 12198f44cb0SIgor Mitsyanko 12298f44cb0SIgor Mitsyanko /* Stop data */ 12398f44cb0SIgor Mitsyanko netif_tx_stop_all_queues(netdev); 12498f44cb0SIgor Mitsyanko if (netif_carrier_ok(netdev)) 12598f44cb0SIgor Mitsyanko netif_carrier_off(netdev); 12698f44cb0SIgor Mitsyanko 12798f44cb0SIgor Mitsyanko if (netdev->reg_state == NETREG_REGISTERED) 12898f44cb0SIgor Mitsyanko unregister_netdevice(netdev); 12998f44cb0SIgor Mitsyanko 13098f44cb0SIgor Mitsyanko vif->netdev->ieee80211_ptr = NULL; 13198f44cb0SIgor Mitsyanko vif->netdev = NULL; 13298f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 13398f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 13498f44cb0SIgor Mitsyanko 13598f44cb0SIgor Mitsyanko return 0; 13698f44cb0SIgor Mitsyanko } 13798f44cb0SIgor Mitsyanko 13898f44cb0SIgor Mitsyanko static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, 13998f44cb0SIgor Mitsyanko const char *name, 14098f44cb0SIgor Mitsyanko unsigned char name_assign_t, 14198f44cb0SIgor Mitsyanko enum nl80211_iftype type, 14298f44cb0SIgor Mitsyanko struct vif_params *params) 14398f44cb0SIgor Mitsyanko { 14498f44cb0SIgor Mitsyanko struct qtnf_wmac *mac; 14598f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 14698f44cb0SIgor Mitsyanko u8 *mac_addr = NULL; 14798f44cb0SIgor Mitsyanko 14898f44cb0SIgor Mitsyanko mac = wiphy_priv(wiphy); 14998f44cb0SIgor Mitsyanko 15098f44cb0SIgor Mitsyanko if (!mac) 15198f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 15298f44cb0SIgor Mitsyanko 15398f44cb0SIgor Mitsyanko switch (type) { 15498f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 15598f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 15698f44cb0SIgor Mitsyanko vif = qtnf_mac_get_free_vif(mac); 15798f44cb0SIgor Mitsyanko if (!vif) { 15898f44cb0SIgor Mitsyanko pr_err("MAC%u: no free VIF available\n", mac->macid); 15998f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 16098f44cb0SIgor Mitsyanko } 16198f44cb0SIgor Mitsyanko 16298f44cb0SIgor Mitsyanko eth_zero_addr(vif->mac_addr); 16398f44cb0SIgor Mitsyanko vif->bss_priority = QTNF_DEF_BSS_PRIORITY; 16498f44cb0SIgor Mitsyanko vif->wdev.wiphy = wiphy; 16598f44cb0SIgor Mitsyanko vif->wdev.iftype = type; 16698f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 16798f44cb0SIgor Mitsyanko break; 16898f44cb0SIgor Mitsyanko default: 16998f44cb0SIgor Mitsyanko pr_err("MAC%u: unsupported IF type %d\n", mac->macid, type); 17098f44cb0SIgor Mitsyanko return ERR_PTR(-ENOTSUPP); 17198f44cb0SIgor Mitsyanko } 17298f44cb0SIgor Mitsyanko 17398f44cb0SIgor Mitsyanko if (params) 17498f44cb0SIgor Mitsyanko mac_addr = params->macaddr; 17598f44cb0SIgor Mitsyanko 17698f44cb0SIgor Mitsyanko if (qtnf_cmd_send_add_intf(vif, type, mac_addr)) { 17798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add VIF\n", mac->macid, vif->vifid); 17898f44cb0SIgor Mitsyanko goto err_cmd; 17998f44cb0SIgor Mitsyanko } 18098f44cb0SIgor Mitsyanko 18198f44cb0SIgor Mitsyanko if (!is_valid_ether_addr(vif->mac_addr)) { 18298f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: FW reported bad MAC: %pM\n", 18398f44cb0SIgor Mitsyanko mac->macid, vif->vifid, vif->mac_addr); 18498f44cb0SIgor Mitsyanko goto err_mac; 18598f44cb0SIgor Mitsyanko } 18698f44cb0SIgor Mitsyanko 18798f44cb0SIgor Mitsyanko if (qtnf_core_net_attach(mac, vif, name, name_assign_t, type)) { 18898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid, 18998f44cb0SIgor Mitsyanko vif->vifid); 19098f44cb0SIgor Mitsyanko goto err_net; 19198f44cb0SIgor Mitsyanko } 19298f44cb0SIgor Mitsyanko 19398f44cb0SIgor Mitsyanko vif->wdev.netdev = vif->netdev; 19498f44cb0SIgor Mitsyanko return &vif->wdev; 19598f44cb0SIgor Mitsyanko 19698f44cb0SIgor Mitsyanko err_net: 19798f44cb0SIgor Mitsyanko vif->netdev = NULL; 19898f44cb0SIgor Mitsyanko err_mac: 19998f44cb0SIgor Mitsyanko qtnf_cmd_send_del_intf(vif); 20098f44cb0SIgor Mitsyanko err_cmd: 20198f44cb0SIgor Mitsyanko vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; 20298f44cb0SIgor Mitsyanko 20398f44cb0SIgor Mitsyanko return ERR_PTR(-EFAULT); 20498f44cb0SIgor Mitsyanko } 20598f44cb0SIgor Mitsyanko 20698f44cb0SIgor Mitsyanko static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, 20798f44cb0SIgor Mitsyanko const struct cfg80211_beacon_data *info) 20898f44cb0SIgor Mitsyanko { 20998f44cb0SIgor Mitsyanko int ret = 0; 21098f44cb0SIgor Mitsyanko 21198f44cb0SIgor Mitsyanko if (!info->beacon_ies || !info->beacon_ies_len) { 21298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, 21398f44cb0SIgor Mitsyanko NULL, 0); 21498f44cb0SIgor Mitsyanko } else { 21598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, 21698f44cb0SIgor Mitsyanko info->beacon_ies, 21798f44cb0SIgor Mitsyanko info->beacon_ies_len); 21898f44cb0SIgor Mitsyanko } 21998f44cb0SIgor Mitsyanko 22098f44cb0SIgor Mitsyanko if (ret) 22198f44cb0SIgor Mitsyanko goto out; 22298f44cb0SIgor Mitsyanko 22398f44cb0SIgor Mitsyanko if (!info->proberesp_ies || !info->proberesp_ies_len) { 22498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 22598f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_PROBE_RESP, 22698f44cb0SIgor Mitsyanko NULL, 0); 22798f44cb0SIgor Mitsyanko } else { 22898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 22998f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_PROBE_RESP, 23098f44cb0SIgor Mitsyanko info->proberesp_ies, 23198f44cb0SIgor Mitsyanko info->proberesp_ies_len); 23298f44cb0SIgor Mitsyanko } 23398f44cb0SIgor Mitsyanko 23498f44cb0SIgor Mitsyanko if (ret) 23598f44cb0SIgor Mitsyanko goto out; 23698f44cb0SIgor Mitsyanko 23798f44cb0SIgor Mitsyanko if (!info->assocresp_ies || !info->assocresp_ies_len) { 23898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 23998f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_ASSOC_RESP, 24098f44cb0SIgor Mitsyanko NULL, 0); 24198f44cb0SIgor Mitsyanko } else { 24298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_mgmt_set_appie(vif, 24398f44cb0SIgor Mitsyanko QLINK_MGMT_FRAME_ASSOC_RESP, 24498f44cb0SIgor Mitsyanko info->assocresp_ies, 24598f44cb0SIgor Mitsyanko info->assocresp_ies_len); 24698f44cb0SIgor Mitsyanko } 24798f44cb0SIgor Mitsyanko 24898f44cb0SIgor Mitsyanko out: 24998f44cb0SIgor Mitsyanko return ret; 25098f44cb0SIgor Mitsyanko } 25198f44cb0SIgor Mitsyanko 25298f44cb0SIgor Mitsyanko static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, 25398f44cb0SIgor Mitsyanko struct cfg80211_beacon_data *info) 25498f44cb0SIgor Mitsyanko { 25598f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 25698f44cb0SIgor Mitsyanko 25798f44cb0SIgor Mitsyanko if (!(vif->bss_status & QTNF_STATE_AP_START)) { 25898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: not started\n", vif->mac->macid, vif->vifid); 25998f44cb0SIgor Mitsyanko return -EFAULT; 26098f44cb0SIgor Mitsyanko } 26198f44cb0SIgor Mitsyanko 26298f44cb0SIgor Mitsyanko return qtnf_mgmt_set_appie(vif, info); 26398f44cb0SIgor Mitsyanko } 26498f44cb0SIgor Mitsyanko 26598f44cb0SIgor Mitsyanko static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, 26698f44cb0SIgor Mitsyanko struct cfg80211_ap_settings *settings) 26798f44cb0SIgor Mitsyanko { 26898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 26934f1145bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 27098f44cb0SIgor Mitsyanko struct qtnf_bss_config *bss_cfg; 27198f44cb0SIgor Mitsyanko int ret; 27298f44cb0SIgor Mitsyanko 27334f1145bSSergey Matyukevich if (!cfg80211_chandef_identical(&mac->chandef, &settings->chandef)) { 27434f1145bSSergey Matyukevich memcpy(&mac->chandef, &settings->chandef, sizeof(mac->chandef)); 27534f1145bSSergey Matyukevich if (vif->vifid != 0) 27634f1145bSSergey Matyukevich pr_warn("%s: unexpected chan %u (%u MHz)\n", dev->name, 27734f1145bSSergey Matyukevich settings->chandef.chan->hw_value, 27834f1145bSSergey Matyukevich settings->chandef.chan->center_freq); 27934f1145bSSergey Matyukevich } 28098f44cb0SIgor Mitsyanko 28134f1145bSSergey Matyukevich bss_cfg = &vif->bss_cfg; 28298f44cb0SIgor Mitsyanko memset(bss_cfg, 0, sizeof(*bss_cfg)); 28398f44cb0SIgor Mitsyanko 28498f44cb0SIgor Mitsyanko bss_cfg->bcn_period = settings->beacon_interval; 28598f44cb0SIgor Mitsyanko bss_cfg->dtim = settings->dtim_period; 28698f44cb0SIgor Mitsyanko bss_cfg->auth_type = settings->auth_type; 28798f44cb0SIgor Mitsyanko bss_cfg->privacy = settings->privacy; 28898f44cb0SIgor Mitsyanko 28998f44cb0SIgor Mitsyanko bss_cfg->ssid_len = settings->ssid_len; 29098f44cb0SIgor Mitsyanko memcpy(&bss_cfg->ssid, settings->ssid, bss_cfg->ssid_len); 29198f44cb0SIgor Mitsyanko 29298f44cb0SIgor Mitsyanko memcpy(&bss_cfg->crypto, &settings->crypto, 29398f44cb0SIgor Mitsyanko sizeof(struct cfg80211_crypto_settings)); 29498f44cb0SIgor Mitsyanko 29598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_config_ap(vif); 29698f44cb0SIgor Mitsyanko if (ret) { 29798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to push config to FW\n", 29898f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 29998f44cb0SIgor Mitsyanko goto out; 30098f44cb0SIgor Mitsyanko } 30198f44cb0SIgor Mitsyanko 30298f44cb0SIgor Mitsyanko if (!(vif->bss_status & QTNF_STATE_AP_CONFIG)) { 30398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: AP config failed in FW\n", vif->mac->macid, 30498f44cb0SIgor Mitsyanko vif->vifid); 30598f44cb0SIgor Mitsyanko ret = -EFAULT; 30698f44cb0SIgor Mitsyanko goto out; 30798f44cb0SIgor Mitsyanko } 30898f44cb0SIgor Mitsyanko 30998f44cb0SIgor Mitsyanko ret = qtnf_mgmt_set_appie(vif, &settings->beacon); 31098f44cb0SIgor Mitsyanko if (ret) { 31198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add IEs to beacon\n", 31298f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 31398f44cb0SIgor Mitsyanko goto out; 31498f44cb0SIgor Mitsyanko } 31598f44cb0SIgor Mitsyanko 31698f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_start_ap(vif); 31798f44cb0SIgor Mitsyanko if (ret) { 31898f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, 31998f44cb0SIgor Mitsyanko vif->vifid); 32098f44cb0SIgor Mitsyanko goto out; 32198f44cb0SIgor Mitsyanko } 32298f44cb0SIgor Mitsyanko 32398f44cb0SIgor Mitsyanko if (!(vif->bss_status & QTNF_STATE_AP_START)) { 32498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: FW failed to start AP operation\n", 32598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 32698f44cb0SIgor Mitsyanko ret = -EFAULT; 32798f44cb0SIgor Mitsyanko } 32898f44cb0SIgor Mitsyanko 32998f44cb0SIgor Mitsyanko out: 33098f44cb0SIgor Mitsyanko return ret; 33198f44cb0SIgor Mitsyanko } 33298f44cb0SIgor Mitsyanko 33398f44cb0SIgor Mitsyanko static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) 33498f44cb0SIgor Mitsyanko { 33598f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 33698f44cb0SIgor Mitsyanko int ret; 33798f44cb0SIgor Mitsyanko 33898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_stop_ap(vif); 33998f44cb0SIgor Mitsyanko if (ret) { 34098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to stop AP operation in FW\n", 34198f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid); 34298f44cb0SIgor Mitsyanko vif->bss_status &= ~QTNF_STATE_AP_START; 34398f44cb0SIgor Mitsyanko vif->bss_status &= ~QTNF_STATE_AP_CONFIG; 34498f44cb0SIgor Mitsyanko 34598f44cb0SIgor Mitsyanko netif_carrier_off(vif->netdev); 34698f44cb0SIgor Mitsyanko } 34798f44cb0SIgor Mitsyanko 34898f44cb0SIgor Mitsyanko return ret; 34998f44cb0SIgor Mitsyanko } 35098f44cb0SIgor Mitsyanko 35198f44cb0SIgor Mitsyanko static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed) 35298f44cb0SIgor Mitsyanko { 35398f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 35498f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 35598f44cb0SIgor Mitsyanko int ret; 35698f44cb0SIgor Mitsyanko 35798f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 35898f44cb0SIgor Mitsyanko if (!vif) { 35998f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 36098f44cb0SIgor Mitsyanko return -EFAULT; 36198f44cb0SIgor Mitsyanko } 36298f44cb0SIgor Mitsyanko 36398f44cb0SIgor Mitsyanko if (changed & (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) { 36498f44cb0SIgor Mitsyanko pr_err("MAC%u: can't modify retry params\n", mac->macid); 36598f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 36698f44cb0SIgor Mitsyanko } 36798f44cb0SIgor Mitsyanko 36898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_update_phy_params(mac, changed); 36998f44cb0SIgor Mitsyanko if (ret) 37098f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to update PHY params\n", mac->macid); 37198f44cb0SIgor Mitsyanko 37298f44cb0SIgor Mitsyanko return ret; 37398f44cb0SIgor Mitsyanko } 37498f44cb0SIgor Mitsyanko 37598f44cb0SIgor Mitsyanko static void 37698f44cb0SIgor Mitsyanko qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, 37798f44cb0SIgor Mitsyanko u16 frame_type, bool reg) 37898f44cb0SIgor Mitsyanko { 37998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 38098f44cb0SIgor Mitsyanko u16 mgmt_type; 38198f44cb0SIgor Mitsyanko u16 new_mask; 38298f44cb0SIgor Mitsyanko u16 qlink_frame_type = 0; 38398f44cb0SIgor Mitsyanko 38498f44cb0SIgor Mitsyanko mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; 38598f44cb0SIgor Mitsyanko 38698f44cb0SIgor Mitsyanko if (reg) 38798f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask | BIT(mgmt_type); 38898f44cb0SIgor Mitsyanko else 38998f44cb0SIgor Mitsyanko new_mask = vif->mgmt_frames_bitmask & ~BIT(mgmt_type); 39098f44cb0SIgor Mitsyanko 39198f44cb0SIgor Mitsyanko if (new_mask == vif->mgmt_frames_bitmask) 39298f44cb0SIgor Mitsyanko return; 39398f44cb0SIgor Mitsyanko 39498f44cb0SIgor Mitsyanko switch (frame_type & IEEE80211_FCTL_STYPE) { 39598f44cb0SIgor Mitsyanko case IEEE80211_STYPE_PROBE_REQ: 39698f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ; 39798f44cb0SIgor Mitsyanko break; 39898f44cb0SIgor Mitsyanko case IEEE80211_STYPE_ACTION: 39998f44cb0SIgor Mitsyanko qlink_frame_type = QLINK_MGMT_FRAME_ACTION; 40098f44cb0SIgor Mitsyanko break; 40198f44cb0SIgor Mitsyanko default: 40298f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported frame type: %X\n", 40398f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, 40498f44cb0SIgor Mitsyanko (frame_type & IEEE80211_FCTL_STYPE) >> 4); 40598f44cb0SIgor Mitsyanko return; 40698f44cb0SIgor Mitsyanko } 40798f44cb0SIgor Mitsyanko 40898f44cb0SIgor Mitsyanko if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) { 40998f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: failed to %sregister mgmt frame type 0x%x\n", 41098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", 41198f44cb0SIgor Mitsyanko frame_type); 41298f44cb0SIgor Mitsyanko return; 41398f44cb0SIgor Mitsyanko } 41498f44cb0SIgor Mitsyanko 41598f44cb0SIgor Mitsyanko vif->mgmt_frames_bitmask = new_mask; 41698f44cb0SIgor Mitsyanko pr_debug("VIF%u.%u: %sregistered mgmt frame type 0x%x\n", 41798f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, reg ? "" : "un", frame_type); 41898f44cb0SIgor Mitsyanko } 41998f44cb0SIgor Mitsyanko 42098f44cb0SIgor Mitsyanko static int 42198f44cb0SIgor Mitsyanko qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 42298f44cb0SIgor Mitsyanko struct cfg80211_mgmt_tx_params *params, u64 *cookie) 42398f44cb0SIgor Mitsyanko { 42498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); 42598f44cb0SIgor Mitsyanko const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; 42698f44cb0SIgor Mitsyanko u32 short_cookie = prandom_u32(); 42798f44cb0SIgor Mitsyanko u16 flags = 0; 42898f44cb0SIgor Mitsyanko 42998f44cb0SIgor Mitsyanko *cookie = short_cookie; 43098f44cb0SIgor Mitsyanko 43198f44cb0SIgor Mitsyanko if (params->offchan) 43298f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN; 43398f44cb0SIgor Mitsyanko 43498f44cb0SIgor Mitsyanko if (params->no_cck) 43598f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_NO_CCK; 43698f44cb0SIgor Mitsyanko 43798f44cb0SIgor Mitsyanko if (params->dont_wait_for_ack) 43898f44cb0SIgor Mitsyanko flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT; 43998f44cb0SIgor Mitsyanko 44098f44cb0SIgor Mitsyanko pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n", 44198f44cb0SIgor Mitsyanko wdev->netdev->name, params->chan->center_freq, 44298f44cb0SIgor Mitsyanko le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da, 44398f44cb0SIgor Mitsyanko params->len, short_cookie, flags); 44498f44cb0SIgor Mitsyanko 44598f44cb0SIgor Mitsyanko return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags, 44698f44cb0SIgor Mitsyanko params->chan->center_freq, 44798f44cb0SIgor Mitsyanko params->buf, params->len); 44898f44cb0SIgor Mitsyanko } 44998f44cb0SIgor Mitsyanko 45098f44cb0SIgor Mitsyanko static int 45198f44cb0SIgor Mitsyanko qtnf_get_station(struct wiphy *wiphy, struct net_device *dev, 45298f44cb0SIgor Mitsyanko const u8 *mac, struct station_info *sinfo) 45398f44cb0SIgor Mitsyanko { 45498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 45598f44cb0SIgor Mitsyanko 45698f44cb0SIgor Mitsyanko return qtnf_cmd_get_sta_info(vif, mac, sinfo); 45798f44cb0SIgor Mitsyanko } 45898f44cb0SIgor Mitsyanko 45998f44cb0SIgor Mitsyanko static int 46098f44cb0SIgor Mitsyanko qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev, 46198f44cb0SIgor Mitsyanko int idx, u8 *mac, struct station_info *sinfo) 46298f44cb0SIgor Mitsyanko { 46398f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 46498f44cb0SIgor Mitsyanko const struct qtnf_sta_node *sta_node; 46598f44cb0SIgor Mitsyanko int ret; 46698f44cb0SIgor Mitsyanko 46798f44cb0SIgor Mitsyanko sta_node = qtnf_sta_list_lookup_index(&vif->sta_list, idx); 46898f44cb0SIgor Mitsyanko 46998f44cb0SIgor Mitsyanko if (unlikely(!sta_node)) 47098f44cb0SIgor Mitsyanko return -ENOENT; 47198f44cb0SIgor Mitsyanko 47298f44cb0SIgor Mitsyanko ether_addr_copy(mac, sta_node->mac_addr); 47398f44cb0SIgor Mitsyanko 47498f44cb0SIgor Mitsyanko ret = qtnf_cmd_get_sta_info(vif, sta_node->mac_addr, sinfo); 47598f44cb0SIgor Mitsyanko 47698f44cb0SIgor Mitsyanko if (unlikely(ret == -ENOENT)) { 47798f44cb0SIgor Mitsyanko qtnf_sta_list_del(&vif->sta_list, mac); 47898f44cb0SIgor Mitsyanko cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL); 47998f44cb0SIgor Mitsyanko sinfo->filled = 0; 48098f44cb0SIgor Mitsyanko } 48198f44cb0SIgor Mitsyanko 48298f44cb0SIgor Mitsyanko return ret; 48398f44cb0SIgor Mitsyanko } 48498f44cb0SIgor Mitsyanko 48598f44cb0SIgor Mitsyanko static int qtnf_add_key(struct wiphy *wiphy, struct net_device *dev, 48698f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr, 48798f44cb0SIgor Mitsyanko struct key_params *params) 48898f44cb0SIgor Mitsyanko { 48998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 49098f44cb0SIgor Mitsyanko int ret; 49198f44cb0SIgor Mitsyanko 49298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params); 49398f44cb0SIgor Mitsyanko if (ret) 49498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to add key: cipher=%x idx=%u pw=%u\n", 49598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->cipher, key_index, 49698f44cb0SIgor Mitsyanko pairwise); 49798f44cb0SIgor Mitsyanko 49898f44cb0SIgor Mitsyanko return ret; 49998f44cb0SIgor Mitsyanko } 50098f44cb0SIgor Mitsyanko 50198f44cb0SIgor Mitsyanko static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev, 50298f44cb0SIgor Mitsyanko u8 key_index, bool pairwise, const u8 *mac_addr) 50398f44cb0SIgor Mitsyanko { 50498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 50598f44cb0SIgor Mitsyanko int ret; 50698f44cb0SIgor Mitsyanko 50798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr); 50898f44cb0SIgor Mitsyanko if (ret) 50998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n", 51098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, pairwise); 51198f44cb0SIgor Mitsyanko 51298f44cb0SIgor Mitsyanko return ret; 51398f44cb0SIgor Mitsyanko } 51498f44cb0SIgor Mitsyanko 51598f44cb0SIgor Mitsyanko static int qtnf_set_default_key(struct wiphy *wiphy, struct net_device *dev, 51698f44cb0SIgor Mitsyanko u8 key_index, bool unicast, bool multicast) 51798f44cb0SIgor Mitsyanko { 51898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 51998f44cb0SIgor Mitsyanko int ret; 52098f44cb0SIgor Mitsyanko 52198f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_key(vif, key_index, unicast, multicast); 52298f44cb0SIgor Mitsyanko if (ret) 52398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set dflt key: idx=%u uc=%u mc=%u\n", 52498f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index, unicast, 52598f44cb0SIgor Mitsyanko multicast); 52698f44cb0SIgor Mitsyanko 52798f44cb0SIgor Mitsyanko return ret; 52898f44cb0SIgor Mitsyanko } 52998f44cb0SIgor Mitsyanko 53098f44cb0SIgor Mitsyanko static int 53198f44cb0SIgor Mitsyanko qtnf_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, 53298f44cb0SIgor Mitsyanko u8 key_index) 53398f44cb0SIgor Mitsyanko { 53498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 53598f44cb0SIgor Mitsyanko int ret; 53698f44cb0SIgor Mitsyanko 53798f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_set_default_mgmt_key(vif, key_index); 53898f44cb0SIgor Mitsyanko if (ret) 53998f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to set default MGMT key: idx=%u\n", 54098f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, key_index); 54198f44cb0SIgor Mitsyanko 54298f44cb0SIgor Mitsyanko return ret; 54398f44cb0SIgor Mitsyanko } 54498f44cb0SIgor Mitsyanko 54598f44cb0SIgor Mitsyanko static int 54698f44cb0SIgor Mitsyanko qtnf_change_station(struct wiphy *wiphy, struct net_device *dev, 54798f44cb0SIgor Mitsyanko const u8 *mac, struct station_parameters *params) 54898f44cb0SIgor Mitsyanko { 54998f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 55098f44cb0SIgor Mitsyanko int ret; 55198f44cb0SIgor Mitsyanko 55298f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_change_sta(vif, mac, params); 55398f44cb0SIgor Mitsyanko if (ret) 55498f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to change STA %pM\n", 55598f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, mac); 55698f44cb0SIgor Mitsyanko 55798f44cb0SIgor Mitsyanko return ret; 55898f44cb0SIgor Mitsyanko } 55998f44cb0SIgor Mitsyanko 56098f44cb0SIgor Mitsyanko static int 56198f44cb0SIgor Mitsyanko qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, 56298f44cb0SIgor Mitsyanko struct station_del_parameters *params) 56398f44cb0SIgor Mitsyanko { 56498f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 56598f44cb0SIgor Mitsyanko int ret; 56698f44cb0SIgor Mitsyanko 56798f44cb0SIgor Mitsyanko if (params->mac && 56898f44cb0SIgor Mitsyanko (vif->wdev.iftype == NL80211_IFTYPE_AP) && 56998f44cb0SIgor Mitsyanko !is_broadcast_ether_addr(params->mac) && 57098f44cb0SIgor Mitsyanko !qtnf_sta_list_lookup(&vif->sta_list, params->mac)) 57198f44cb0SIgor Mitsyanko return 0; 57298f44cb0SIgor Mitsyanko 57398f44cb0SIgor Mitsyanko qtnf_scan_done(vif->mac, true); 57498f44cb0SIgor Mitsyanko 57598f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_del_sta(vif, params); 57698f44cb0SIgor Mitsyanko if (ret) 57798f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to delete STA %pM\n", 57898f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, params->mac); 57998f44cb0SIgor Mitsyanko return ret; 58098f44cb0SIgor Mitsyanko } 58198f44cb0SIgor Mitsyanko 582c7ead2abSSergey Matyukevich static void qtnf_scan_timeout(unsigned long data) 583c7ead2abSSergey Matyukevich { 584c7ead2abSSergey Matyukevich struct qtnf_wmac *mac = (struct qtnf_wmac *)data; 585c7ead2abSSergey Matyukevich 586c7ead2abSSergey Matyukevich pr_warn("mac%d scan timed out\n", mac->macid); 587c7ead2abSSergey Matyukevich qtnf_scan_done(mac, true); 588c7ead2abSSergey Matyukevich } 589c7ead2abSSergey Matyukevich 59098f44cb0SIgor Mitsyanko static int 59198f44cb0SIgor Mitsyanko qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) 59298f44cb0SIgor Mitsyanko { 59398f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 59498f44cb0SIgor Mitsyanko 59598f44cb0SIgor Mitsyanko mac->scan_req = request; 59698f44cb0SIgor Mitsyanko 597c7ead2abSSergey Matyukevich if (qtnf_cmd_send_scan(mac)) { 59898f44cb0SIgor Mitsyanko pr_err("MAC%u: failed to start scan\n", mac->macid); 599c7ead2abSSergey Matyukevich mac->scan_req = NULL; 600c7ead2abSSergey Matyukevich return -EFAULT; 601c7ead2abSSergey Matyukevich } 60298f44cb0SIgor Mitsyanko 603c7ead2abSSergey Matyukevich mac->scan_timeout.data = (unsigned long)mac; 604c7ead2abSSergey Matyukevich mac->scan_timeout.function = qtnf_scan_timeout; 605c7ead2abSSergey Matyukevich mod_timer(&mac->scan_timeout, 606c7ead2abSSergey Matyukevich jiffies + QTNF_SCAN_TIMEOUT_SEC * HZ); 607c7ead2abSSergey Matyukevich 608c7ead2abSSergey Matyukevich return 0; 60998f44cb0SIgor Mitsyanko } 61098f44cb0SIgor Mitsyanko 61198f44cb0SIgor Mitsyanko static int 61298f44cb0SIgor Mitsyanko qtnf_connect(struct wiphy *wiphy, struct net_device *dev, 61398f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 61498f44cb0SIgor Mitsyanko { 61598f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 61698f44cb0SIgor Mitsyanko struct qtnf_bss_config *bss_cfg; 61798f44cb0SIgor Mitsyanko int ret; 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 -EBUSY; 62498f44cb0SIgor Mitsyanko 62598f44cb0SIgor Mitsyanko bss_cfg = &vif->bss_cfg; 62698f44cb0SIgor Mitsyanko memset(bss_cfg, 0, sizeof(*bss_cfg)); 62798f44cb0SIgor Mitsyanko 62898f44cb0SIgor Mitsyanko bss_cfg->ssid_len = sme->ssid_len; 62998f44cb0SIgor Mitsyanko memcpy(&bss_cfg->ssid, sme->ssid, bss_cfg->ssid_len); 63098f44cb0SIgor Mitsyanko bss_cfg->auth_type = sme->auth_type; 63198f44cb0SIgor Mitsyanko bss_cfg->privacy = sme->privacy; 63298f44cb0SIgor Mitsyanko bss_cfg->mfp = sme->mfp; 63398f44cb0SIgor Mitsyanko 63498f44cb0SIgor Mitsyanko if ((sme->bg_scan_period > 0) && 63598f44cb0SIgor Mitsyanko (sme->bg_scan_period <= QTNF_MAX_BG_SCAN_PERIOD)) 63698f44cb0SIgor Mitsyanko bss_cfg->bg_scan_period = sme->bg_scan_period; 63798f44cb0SIgor Mitsyanko else if (sme->bg_scan_period == -1) 63898f44cb0SIgor Mitsyanko bss_cfg->bg_scan_period = QTNF_DEFAULT_BG_SCAN_PERIOD; 63998f44cb0SIgor Mitsyanko else 64098f44cb0SIgor Mitsyanko bss_cfg->bg_scan_period = 0; /* disabled */ 64198f44cb0SIgor Mitsyanko 64298f44cb0SIgor Mitsyanko bss_cfg->connect_flags = 0; 64398f44cb0SIgor Mitsyanko 64498f44cb0SIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_HT) 64598f44cb0SIgor Mitsyanko bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; 64698f44cb0SIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_VHT) 64798f44cb0SIgor Mitsyanko bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; 64898f44cb0SIgor Mitsyanko if (sme->flags & ASSOC_REQ_USE_RRM) 64998f44cb0SIgor Mitsyanko bss_cfg->connect_flags |= QLINK_STA_CONNECT_USE_RRM; 65098f44cb0SIgor Mitsyanko 65198f44cb0SIgor Mitsyanko memcpy(&bss_cfg->crypto, &sme->crypto, sizeof(bss_cfg->crypto)); 65296d4eaf2SIgor Mitsyanko 65398f44cb0SIgor Mitsyanko if (sme->bssid) 65498f44cb0SIgor Mitsyanko ether_addr_copy(bss_cfg->bssid, sme->bssid); 65598f44cb0SIgor Mitsyanko else 65698f44cb0SIgor Mitsyanko eth_zero_addr(bss_cfg->bssid); 65798f44cb0SIgor Mitsyanko 65898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_connect(vif, sme); 65998f44cb0SIgor Mitsyanko if (ret) { 66098f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to connect\n", vif->mac->macid, 66198f44cb0SIgor Mitsyanko vif->vifid); 66298f44cb0SIgor Mitsyanko return ret; 66398f44cb0SIgor Mitsyanko } 66498f44cb0SIgor Mitsyanko 66598f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_CONNECTING; 66698f44cb0SIgor Mitsyanko return 0; 66798f44cb0SIgor Mitsyanko } 66898f44cb0SIgor Mitsyanko 66998f44cb0SIgor Mitsyanko static int 67098f44cb0SIgor Mitsyanko qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, 67198f44cb0SIgor Mitsyanko u16 reason_code) 67298f44cb0SIgor Mitsyanko { 67398f44cb0SIgor Mitsyanko struct qtnf_wmac *mac = wiphy_priv(wiphy); 67498f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 67598f44cb0SIgor Mitsyanko int ret; 67698f44cb0SIgor Mitsyanko 67798f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 67898f44cb0SIgor Mitsyanko if (!vif) { 67998f44cb0SIgor Mitsyanko pr_err("MAC%u: primary VIF is not configured\n", mac->macid); 68098f44cb0SIgor Mitsyanko return -EFAULT; 68198f44cb0SIgor Mitsyanko } 68298f44cb0SIgor Mitsyanko 68398f44cb0SIgor Mitsyanko if (vif->wdev.iftype != NL80211_IFTYPE_STATION) 68498f44cb0SIgor Mitsyanko return -EOPNOTSUPP; 68598f44cb0SIgor Mitsyanko 68698f44cb0SIgor Mitsyanko if (vif->sta_state == QTNF_STA_DISCONNECTED) 68798f44cb0SIgor Mitsyanko return 0; 68898f44cb0SIgor Mitsyanko 68998f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_disconnect(vif, reason_code); 69098f44cb0SIgor Mitsyanko if (ret) { 69198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: failed to disconnect\n", mac->macid, 69298f44cb0SIgor Mitsyanko vif->vifid); 69398f44cb0SIgor Mitsyanko return ret; 69498f44cb0SIgor Mitsyanko } 69598f44cb0SIgor Mitsyanko 69698f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 69798f44cb0SIgor Mitsyanko return 0; 69898f44cb0SIgor Mitsyanko } 69998f44cb0SIgor Mitsyanko 7007c04b439SSergey Matyukevich static int 7017c04b439SSergey Matyukevich qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, 7027c04b439SSergey Matyukevich int idx, struct survey_info *survey) 7037c04b439SSergey Matyukevich { 7047c04b439SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 7057c04b439SSergey Matyukevich struct ieee80211_supported_band *sband; 70634f1145bSSergey Matyukevich struct cfg80211_chan_def *chandef; 7077c04b439SSergey Matyukevich struct ieee80211_channel *chan; 7087c04b439SSergey Matyukevich struct qtnf_chan_stats stats; 70927894448SSergey Matyukevich struct qtnf_vif *vif; 7107c04b439SSergey Matyukevich int ret; 7117c04b439SSergey Matyukevich 71227894448SSergey Matyukevich vif = qtnf_netdev_get_priv(dev); 71334f1145bSSergey Matyukevich chandef = &mac->chandef; 71427894448SSergey Matyukevich 7157c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_2GHZ]; 7167c04b439SSergey Matyukevich if (sband && idx >= sband->n_channels) { 7177c04b439SSergey Matyukevich idx -= sband->n_channels; 7187c04b439SSergey Matyukevich sband = NULL; 7197c04b439SSergey Matyukevich } 7207c04b439SSergey Matyukevich 7217c04b439SSergey Matyukevich if (!sband) 7227c04b439SSergey Matyukevich sband = wiphy->bands[NL80211_BAND_5GHZ]; 7237c04b439SSergey Matyukevich 7247c04b439SSergey Matyukevich if (!sband || idx >= sband->n_channels) 7257c04b439SSergey Matyukevich return -ENOENT; 7267c04b439SSergey Matyukevich 7277c04b439SSergey Matyukevich chan = &sband->channels[idx]; 7287c04b439SSergey Matyukevich memset(&stats, 0, sizeof(stats)); 7297c04b439SSergey Matyukevich 7307c04b439SSergey Matyukevich survey->channel = chan; 7317c04b439SSergey Matyukevich survey->filled = 0x0; 7327c04b439SSergey Matyukevich 73334f1145bSSergey Matyukevich if (chandef->chan) { 73434f1145bSSergey Matyukevich if (chan->hw_value == chandef->chan->hw_value) 73534f1145bSSergey Matyukevich survey->filled = SURVEY_INFO_IN_USE; 73634f1145bSSergey Matyukevich } 73727894448SSergey Matyukevich 7387c04b439SSergey Matyukevich ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); 7397c04b439SSergey Matyukevich switch (ret) { 7407c04b439SSergey Matyukevich case 0: 7417c04b439SSergey Matyukevich if (unlikely(stats.chan_num != chan->hw_value)) { 7427c04b439SSergey Matyukevich pr_err("received stats for channel %d instead of %d\n", 7437c04b439SSergey Matyukevich stats.chan_num, chan->hw_value); 7447c04b439SSergey Matyukevich ret = -EINVAL; 7457c04b439SSergey Matyukevich break; 7467c04b439SSergey Matyukevich } 7477c04b439SSergey Matyukevich 74834f1145bSSergey Matyukevich survey->filled |= SURVEY_INFO_TIME | 7497c04b439SSergey Matyukevich SURVEY_INFO_TIME_SCAN | 7507c04b439SSergey Matyukevich SURVEY_INFO_TIME_BUSY | 7517c04b439SSergey Matyukevich SURVEY_INFO_TIME_RX | 7527c04b439SSergey Matyukevich SURVEY_INFO_TIME_TX | 7537c04b439SSergey Matyukevich SURVEY_INFO_NOISE_DBM; 7547c04b439SSergey Matyukevich 7557c04b439SSergey Matyukevich survey->time_scan = stats.cca_try; 7567c04b439SSergey Matyukevich survey->time = stats.cca_try; 7577c04b439SSergey Matyukevich survey->time_tx = stats.cca_tx; 7587c04b439SSergey Matyukevich survey->time_rx = stats.cca_rx; 7597c04b439SSergey Matyukevich survey->time_busy = stats.cca_busy; 7607c04b439SSergey Matyukevich survey->noise = stats.chan_noise; 7617c04b439SSergey Matyukevich break; 7627c04b439SSergey Matyukevich case -ENOENT: 7637c04b439SSergey Matyukevich pr_debug("no stats for channel %u\n", chan->hw_value); 7647c04b439SSergey Matyukevich ret = 0; 7657c04b439SSergey Matyukevich break; 7667c04b439SSergey Matyukevich default: 7677c04b439SSergey Matyukevich pr_debug("failed to get chan(%d) stats from card\n", 7687c04b439SSergey Matyukevich chan->hw_value); 7697c04b439SSergey Matyukevich ret = -EINVAL; 7707c04b439SSergey Matyukevich break; 7717c04b439SSergey Matyukevich } 7727c04b439SSergey Matyukevich 7737c04b439SSergey Matyukevich return ret; 7747c04b439SSergey Matyukevich } 7757c04b439SSergey Matyukevich 77627894448SSergey Matyukevich static int 77727894448SSergey Matyukevich qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, 77827894448SSergey Matyukevich struct cfg80211_chan_def *chandef) 77927894448SSergey Matyukevich { 78034f1145bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 78127894448SSergey Matyukevich struct net_device *ndev = wdev->netdev; 78227894448SSergey Matyukevich struct qtnf_vif *vif; 7839e5478b6SIgor Mitsyanko int ret; 78427894448SSergey Matyukevich 78527894448SSergey Matyukevich if (!ndev) 78627894448SSergey Matyukevich return -ENODEV; 78727894448SSergey Matyukevich 78827894448SSergey Matyukevich vif = qtnf_netdev_get_priv(wdev->netdev); 78927894448SSergey Matyukevich 7909e5478b6SIgor Mitsyanko ret = qtnf_cmd_get_channel(vif, chandef); 7919e5478b6SIgor Mitsyanko if (ret) { 7929e5478b6SIgor Mitsyanko pr_err("%s: failed to get channel: %d\n", ndev->name, ret); 7939e5478b6SIgor Mitsyanko goto out; 79427894448SSergey Matyukevich } 79527894448SSergey Matyukevich 7969e5478b6SIgor Mitsyanko if (!cfg80211_chandef_valid(chandef)) { 7979e5478b6SIgor Mitsyanko pr_err("%s: bad chan freq1=%u freq2=%u bw=%u\n", ndev->name, 7989e5478b6SIgor Mitsyanko chandef->center_freq1, chandef->center_freq2, 7999e5478b6SIgor Mitsyanko chandef->width); 8009e5478b6SIgor Mitsyanko ret = -ENODATA; 80134f1145bSSergey Matyukevich } 80234f1145bSSergey Matyukevich 8039e5478b6SIgor Mitsyanko memcpy(&mac->chandef, chandef, sizeof(mac->chandef)); 8049e5478b6SIgor Mitsyanko 8059e5478b6SIgor Mitsyanko out: 8069e5478b6SIgor Mitsyanko return ret; 80727894448SSergey Matyukevich } 80827894448SSergey Matyukevich 80997883695SSergey Matyukevich static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, 81097883695SSergey Matyukevich struct cfg80211_csa_settings *params) 81197883695SSergey Matyukevich { 81297883695SSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy); 81397883695SSergey Matyukevich struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); 81497883695SSergey Matyukevich int ret; 81597883695SSergey Matyukevich 81697883695SSergey Matyukevich pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, 81797883695SSergey Matyukevich params->chandef.chan->hw_value, params->count, 81897883695SSergey Matyukevich params->radar_required, params->block_tx); 81997883695SSergey Matyukevich 82097883695SSergey Matyukevich switch (vif->wdev.iftype) { 82197883695SSergey Matyukevich case NL80211_IFTYPE_AP: 82297883695SSergey Matyukevich if (!(vif->bss_status & QTNF_STATE_AP_START)) { 82397883695SSergey Matyukevich pr_warn("AP not started on %s\n", dev->name); 82497883695SSergey Matyukevich return -ENOTCONN; 82597883695SSergey Matyukevich } 82697883695SSergey Matyukevich break; 82797883695SSergey Matyukevich default: 82897883695SSergey Matyukevich pr_err("unsupported vif type (%d) on %s\n", 82997883695SSergey Matyukevich vif->wdev.iftype, dev->name); 83097883695SSergey Matyukevich return -EOPNOTSUPP; 83197883695SSergey Matyukevich } 83297883695SSergey Matyukevich 83397883695SSergey Matyukevich if (vif->vifid != 0) { 83497883695SSergey Matyukevich if (!(mac->status & QTNF_MAC_CSA_ACTIVE)) 83597883695SSergey Matyukevich return -EOPNOTSUPP; 83697883695SSergey Matyukevich 83797883695SSergey Matyukevich if (!cfg80211_chandef_identical(¶ms->chandef, 83897883695SSergey Matyukevich &mac->csa_chandef)) 83997883695SSergey Matyukevich return -EINVAL; 84097883695SSergey Matyukevich 84197883695SSergey Matyukevich return 0; 84297883695SSergey Matyukevich } 84397883695SSergey Matyukevich 84497883695SSergey Matyukevich if (!cfg80211_chandef_valid(¶ms->chandef)) { 84597883695SSergey Matyukevich pr_err("%s: invalid channel\n", dev->name); 84697883695SSergey Matyukevich return -EINVAL; 84797883695SSergey Matyukevich } 84897883695SSergey Matyukevich 8498c015b90SIgor Mitsyanko ret = qtnf_cmd_send_chan_switch(vif, params); 85097883695SSergey Matyukevich if (ret) 85197883695SSergey Matyukevich pr_warn("%s: failed to switch to channel (%u)\n", 85297883695SSergey Matyukevich dev->name, params->chandef.chan->hw_value); 85397883695SSergey Matyukevich 85497883695SSergey Matyukevich return ret; 85597883695SSergey Matyukevich } 85697883695SSergey Matyukevich 85798f44cb0SIgor Mitsyanko static struct cfg80211_ops qtn_cfg80211_ops = { 85898f44cb0SIgor Mitsyanko .add_virtual_intf = qtnf_add_virtual_intf, 85998f44cb0SIgor Mitsyanko .change_virtual_intf = qtnf_change_virtual_intf, 86098f44cb0SIgor Mitsyanko .del_virtual_intf = qtnf_del_virtual_intf, 86198f44cb0SIgor Mitsyanko .start_ap = qtnf_start_ap, 86298f44cb0SIgor Mitsyanko .change_beacon = qtnf_change_beacon, 86398f44cb0SIgor Mitsyanko .stop_ap = qtnf_stop_ap, 86498f44cb0SIgor Mitsyanko .set_wiphy_params = qtnf_set_wiphy_params, 86598f44cb0SIgor Mitsyanko .mgmt_frame_register = qtnf_mgmt_frame_register, 86698f44cb0SIgor Mitsyanko .mgmt_tx = qtnf_mgmt_tx, 86798f44cb0SIgor Mitsyanko .change_station = qtnf_change_station, 86898f44cb0SIgor Mitsyanko .del_station = qtnf_del_station, 86998f44cb0SIgor Mitsyanko .get_station = qtnf_get_station, 87098f44cb0SIgor Mitsyanko .dump_station = qtnf_dump_station, 87198f44cb0SIgor Mitsyanko .add_key = qtnf_add_key, 87298f44cb0SIgor Mitsyanko .del_key = qtnf_del_key, 87398f44cb0SIgor Mitsyanko .set_default_key = qtnf_set_default_key, 87498f44cb0SIgor Mitsyanko .set_default_mgmt_key = qtnf_set_default_mgmt_key, 87598f44cb0SIgor Mitsyanko .scan = qtnf_scan, 87698f44cb0SIgor Mitsyanko .connect = qtnf_connect, 8777c04b439SSergey Matyukevich .disconnect = qtnf_disconnect, 87827894448SSergey Matyukevich .dump_survey = qtnf_dump_survey, 87997883695SSergey Matyukevich .get_channel = qtnf_get_channel, 88097883695SSergey Matyukevich .channel_switch = qtnf_channel_switch 88198f44cb0SIgor Mitsyanko }; 88298f44cb0SIgor Mitsyanko 8834dd07d2bSSergey Matyukevich static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, 88498f44cb0SIgor Mitsyanko struct regulatory_request *req) 88598f44cb0SIgor Mitsyanko { 8864dd07d2bSSergey Matyukevich struct qtnf_wmac *mac = wiphy_priv(wiphy_in); 8874dd07d2bSSergey Matyukevich struct qtnf_bus *bus = mac->bus; 8884dd07d2bSSergey Matyukevich struct wiphy *wiphy; 8894dd07d2bSSergey Matyukevich unsigned int mac_idx; 89098f44cb0SIgor Mitsyanko enum nl80211_band band; 8914dd07d2bSSergey Matyukevich int ret; 89298f44cb0SIgor Mitsyanko 89398f44cb0SIgor Mitsyanko pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, 89498f44cb0SIgor Mitsyanko req->alpha2[0], req->alpha2[1]); 89598f44cb0SIgor Mitsyanko 8964dd07d2bSSergey Matyukevich ret = qtnf_cmd_reg_notify(bus, req); 8974dd07d2bSSergey Matyukevich if (ret) { 8984dd07d2bSSergey Matyukevich if (ret != -EOPNOTSUPP && ret != -EALREADY) 8994dd07d2bSSergey Matyukevich pr_err("failed to update reg domain to %c%c\n", 9004dd07d2bSSergey Matyukevich req->alpha2[0], req->alpha2[1]); 90198f44cb0SIgor Mitsyanko return; 90298f44cb0SIgor Mitsyanko } 90398f44cb0SIgor Mitsyanko 9044dd07d2bSSergey Matyukevich for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { 9054dd07d2bSSergey Matyukevich if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) 90698f44cb0SIgor Mitsyanko continue; 90798f44cb0SIgor Mitsyanko 9084dd07d2bSSergey Matyukevich mac = bus->mac[mac_idx]; 9094dd07d2bSSergey Matyukevich wiphy = priv_to_wiphy(mac); 91098f44cb0SIgor Mitsyanko 91198f44cb0SIgor Mitsyanko for (band = 0; band < NUM_NL80211_BANDS; ++band) { 91298f44cb0SIgor Mitsyanko if (!wiphy->bands[band]) 91398f44cb0SIgor Mitsyanko continue; 91498f44cb0SIgor Mitsyanko 9154dd07d2bSSergey Matyukevich ret = qtnf_cmd_get_mac_chan_info(mac, 9164dd07d2bSSergey Matyukevich wiphy->bands[band]); 9174dd07d2bSSergey Matyukevich if (ret) 9184dd07d2bSSergey Matyukevich pr_err("failed to get chan info for mac %u band %u\n", 9194dd07d2bSSergey Matyukevich mac_idx, band); 92098f44cb0SIgor Mitsyanko } 92198f44cb0SIgor Mitsyanko } 92298f44cb0SIgor Mitsyanko } 92398f44cb0SIgor Mitsyanko 92498f44cb0SIgor Mitsyanko void qtnf_band_setup_htvht_caps(struct qtnf_mac_info *macinfo, 92598f44cb0SIgor Mitsyanko struct ieee80211_supported_band *band) 92698f44cb0SIgor Mitsyanko { 92798f44cb0SIgor Mitsyanko struct ieee80211_sta_ht_cap *ht_cap; 92898f44cb0SIgor Mitsyanko struct ieee80211_sta_vht_cap *vht_cap; 92998f44cb0SIgor Mitsyanko 93098f44cb0SIgor Mitsyanko ht_cap = &band->ht_cap; 93198f44cb0SIgor Mitsyanko ht_cap->ht_supported = true; 93298f44cb0SIgor Mitsyanko memcpy(&ht_cap->cap, &macinfo->ht_cap.cap_info, 93398f44cb0SIgor Mitsyanko sizeof(u16)); 93498f44cb0SIgor Mitsyanko ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; 93598f44cb0SIgor Mitsyanko ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; 93698f44cb0SIgor Mitsyanko memcpy(&ht_cap->mcs, &macinfo->ht_cap.mcs, 93798f44cb0SIgor Mitsyanko sizeof(ht_cap->mcs)); 93898f44cb0SIgor Mitsyanko 93998f44cb0SIgor Mitsyanko if (macinfo->phymode_cap & QLINK_PHYMODE_AC) { 94098f44cb0SIgor Mitsyanko vht_cap = &band->vht_cap; 94198f44cb0SIgor Mitsyanko vht_cap->vht_supported = true; 94298f44cb0SIgor Mitsyanko memcpy(&vht_cap->cap, 94398f44cb0SIgor Mitsyanko &macinfo->vht_cap.vht_cap_info, sizeof(u32)); 94498f44cb0SIgor Mitsyanko /* Update MCS support for VHT */ 94598f44cb0SIgor Mitsyanko memcpy(&vht_cap->vht_mcs, 94698f44cb0SIgor Mitsyanko &macinfo->vht_cap.supp_mcs, 94798f44cb0SIgor Mitsyanko sizeof(struct ieee80211_vht_mcs_info)); 94898f44cb0SIgor Mitsyanko } 94998f44cb0SIgor Mitsyanko } 95098f44cb0SIgor Mitsyanko 95198f44cb0SIgor Mitsyanko struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) 95298f44cb0SIgor Mitsyanko { 95398f44cb0SIgor Mitsyanko struct wiphy *wiphy; 95498f44cb0SIgor Mitsyanko 95598f44cb0SIgor Mitsyanko wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac)); 95698f44cb0SIgor Mitsyanko if (!wiphy) 95798f44cb0SIgor Mitsyanko return NULL; 95898f44cb0SIgor Mitsyanko 95998f44cb0SIgor Mitsyanko set_wiphy_dev(wiphy, bus->dev); 96098f44cb0SIgor Mitsyanko 96198f44cb0SIgor Mitsyanko return wiphy; 96298f44cb0SIgor Mitsyanko } 96398f44cb0SIgor Mitsyanko 96498f44cb0SIgor Mitsyanko static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, 96598f44cb0SIgor Mitsyanko struct ieee80211_iface_combination *if_comb, 96698f44cb0SIgor Mitsyanko const struct qtnf_mac_info *mac_info) 96798f44cb0SIgor Mitsyanko { 96898f44cb0SIgor Mitsyanko size_t max_interfaces = 0; 96998f44cb0SIgor Mitsyanko u16 interface_modes = 0; 97098f44cb0SIgor Mitsyanko size_t i; 97198f44cb0SIgor Mitsyanko 97298f44cb0SIgor Mitsyanko if (unlikely(!mac_info->limits || !mac_info->n_limits)) 97398f44cb0SIgor Mitsyanko return -ENOENT; 97498f44cb0SIgor Mitsyanko 97598f44cb0SIgor Mitsyanko if_comb->limits = mac_info->limits; 97698f44cb0SIgor Mitsyanko if_comb->n_limits = mac_info->n_limits; 97798f44cb0SIgor Mitsyanko 97898f44cb0SIgor Mitsyanko for (i = 0; i < mac_info->n_limits; i++) { 97998f44cb0SIgor Mitsyanko max_interfaces += mac_info->limits[i].max; 98098f44cb0SIgor Mitsyanko interface_modes |= mac_info->limits[i].types; 98198f44cb0SIgor Mitsyanko } 98298f44cb0SIgor Mitsyanko 98398f44cb0SIgor Mitsyanko if_comb->num_different_channels = 1; 98498f44cb0SIgor Mitsyanko if_comb->beacon_int_infra_match = true; 98598f44cb0SIgor Mitsyanko if_comb->max_interfaces = max_interfaces; 98698f44cb0SIgor Mitsyanko if_comb->radar_detect_widths = mac_info->radar_detect_widths; 98798f44cb0SIgor Mitsyanko wiphy->interface_modes = interface_modes; 98898f44cb0SIgor Mitsyanko 98998f44cb0SIgor Mitsyanko return 0; 99098f44cb0SIgor Mitsyanko } 99198f44cb0SIgor Mitsyanko 99298f44cb0SIgor Mitsyanko int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) 99398f44cb0SIgor Mitsyanko { 99498f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 99598f44cb0SIgor Mitsyanko struct ieee80211_iface_combination *iface_comb = NULL; 99698f44cb0SIgor Mitsyanko int ret; 99798f44cb0SIgor Mitsyanko 99898f44cb0SIgor Mitsyanko if (!wiphy) { 99998f44cb0SIgor Mitsyanko pr_err("invalid wiphy pointer\n"); 100098f44cb0SIgor Mitsyanko return -EFAULT; 100198f44cb0SIgor Mitsyanko } 100298f44cb0SIgor Mitsyanko 100398f44cb0SIgor Mitsyanko iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); 10044dd07d2bSSergey Matyukevich if (!iface_comb) 10054dd07d2bSSergey Matyukevich return -ENOMEM; 100698f44cb0SIgor Mitsyanko 100798f44cb0SIgor Mitsyanko ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); 100898f44cb0SIgor Mitsyanko if (ret) 100998f44cb0SIgor Mitsyanko goto out; 101098f44cb0SIgor Mitsyanko 101198f44cb0SIgor Mitsyanko pr_info("MAC%u: phymode=%#x radar=%#x\n", mac->macid, 101298f44cb0SIgor Mitsyanko mac->macinfo.phymode_cap, mac->macinfo.radar_detect_widths); 101398f44cb0SIgor Mitsyanko 101498f44cb0SIgor Mitsyanko wiphy->frag_threshold = mac->macinfo.frag_thr; 101598f44cb0SIgor Mitsyanko wiphy->rts_threshold = mac->macinfo.rts_thr; 101698f44cb0SIgor Mitsyanko wiphy->retry_short = mac->macinfo.sretry_limit; 101798f44cb0SIgor Mitsyanko wiphy->retry_long = mac->macinfo.lretry_limit; 101898f44cb0SIgor Mitsyanko wiphy->coverage_class = mac->macinfo.coverage_class; 101998f44cb0SIgor Mitsyanko 102098f44cb0SIgor Mitsyanko wiphy->max_scan_ssids = QTNF_MAX_SSID_LIST_LENGTH; 102198f44cb0SIgor Mitsyanko wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; 102298f44cb0SIgor Mitsyanko wiphy->mgmt_stypes = qtnf_mgmt_stypes; 102398f44cb0SIgor Mitsyanko wiphy->max_remain_on_channel_duration = 5000; 102498f44cb0SIgor Mitsyanko 102598f44cb0SIgor Mitsyanko wiphy->iface_combinations = iface_comb; 102698f44cb0SIgor Mitsyanko wiphy->n_iface_combinations = 1; 102797883695SSergey Matyukevich wiphy->max_num_csa_counters = 2; 102898f44cb0SIgor Mitsyanko 102998f44cb0SIgor Mitsyanko /* Initialize cipher suits */ 103098f44cb0SIgor Mitsyanko wiphy->cipher_suites = qtnf_cipher_suites; 103198f44cb0SIgor Mitsyanko wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); 103298f44cb0SIgor Mitsyanko wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 103398f44cb0SIgor Mitsyanko wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | 103498f44cb0SIgor Mitsyanko WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | 103597883695SSergey Matyukevich WIPHY_FLAG_AP_UAPSD | 103697883695SSergey Matyukevich WIPHY_FLAG_HAS_CHANNEL_SWITCH; 103798f44cb0SIgor Mitsyanko 103898f44cb0SIgor Mitsyanko wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | 103998f44cb0SIgor Mitsyanko NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; 104098f44cb0SIgor Mitsyanko 104198f44cb0SIgor Mitsyanko wiphy->available_antennas_tx = mac->macinfo.num_tx_chain; 104298f44cb0SIgor Mitsyanko wiphy->available_antennas_rx = mac->macinfo.num_rx_chain; 104398f44cb0SIgor Mitsyanko 104498f44cb0SIgor Mitsyanko wiphy->max_ap_assoc_sta = mac->macinfo.max_ap_assoc_sta; 104598f44cb0SIgor Mitsyanko 104698f44cb0SIgor Mitsyanko ether_addr_copy(wiphy->perm_addr, mac->macaddr); 104798f44cb0SIgor Mitsyanko 104898f44cb0SIgor Mitsyanko if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) { 10494dd07d2bSSergey Matyukevich wiphy->regulatory_flags |= REGULATORY_STRICT_REG | 10504dd07d2bSSergey Matyukevich REGULATORY_CUSTOM_REG; 105198f44cb0SIgor Mitsyanko wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; 10524dd07d2bSSergey Matyukevich wiphy_apply_custom_regulatory(wiphy, hw_info->rd); 105398f44cb0SIgor Mitsyanko } else { 105498f44cb0SIgor Mitsyanko wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; 105598f44cb0SIgor Mitsyanko } 105698f44cb0SIgor Mitsyanko 105798f44cb0SIgor Mitsyanko ret = wiphy_register(wiphy); 1058ea19479fSSergey Matyukevich if (ret < 0) 1059ea19479fSSergey Matyukevich goto out; 1060ea19479fSSergey Matyukevich 1061ea19479fSSergey Matyukevich if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) 1062ea19479fSSergey Matyukevich ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); 1063ea19479fSSergey Matyukevich else if (isalpha(hw_info->rd->alpha2[0]) && 1064ea19479fSSergey Matyukevich isalpha(hw_info->rd->alpha2[1])) 1065ea19479fSSergey Matyukevich ret = regulatory_hint(wiphy, hw_info->rd->alpha2); 1066ea19479fSSergey Matyukevich 106798f44cb0SIgor Mitsyanko out: 10684dd07d2bSSergey Matyukevich if (ret) { 106998f44cb0SIgor Mitsyanko kfree(iface_comb); 107098f44cb0SIgor Mitsyanko return ret; 107198f44cb0SIgor Mitsyanko } 107298f44cb0SIgor Mitsyanko 107398f44cb0SIgor Mitsyanko return 0; 107498f44cb0SIgor Mitsyanko } 107598f44cb0SIgor Mitsyanko 107698f44cb0SIgor Mitsyanko void qtnf_netdev_updown(struct net_device *ndev, bool up) 107798f44cb0SIgor Mitsyanko { 107898f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 107998f44cb0SIgor Mitsyanko 108098f44cb0SIgor Mitsyanko if (qtnf_cmd_send_updown_intf(vif, up)) 108198f44cb0SIgor Mitsyanko pr_err("failed to send up/down command to FW\n"); 108298f44cb0SIgor Mitsyanko } 108398f44cb0SIgor Mitsyanko 108498f44cb0SIgor Mitsyanko void qtnf_virtual_intf_cleanup(struct net_device *ndev) 108598f44cb0SIgor Mitsyanko { 108698f44cb0SIgor Mitsyanko struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); 10874f8e2545SColin Ian King struct qtnf_wmac *mac = wiphy_priv(vif->wdev.wiphy); 108898f44cb0SIgor Mitsyanko 108998f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 109098f44cb0SIgor Mitsyanko switch (vif->sta_state) { 109198f44cb0SIgor Mitsyanko case QTNF_STA_DISCONNECTED: 109298f44cb0SIgor Mitsyanko break; 109398f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTING: 109498f44cb0SIgor Mitsyanko cfg80211_connect_result(vif->netdev, 109598f44cb0SIgor Mitsyanko vif->bss_cfg.bssid, NULL, 0, 109698f44cb0SIgor Mitsyanko NULL, 0, 109798f44cb0SIgor Mitsyanko WLAN_STATUS_UNSPECIFIED_FAILURE, 109898f44cb0SIgor Mitsyanko GFP_KERNEL); 109998f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 110098f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 110198f44cb0SIgor Mitsyanko break; 110298f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTED: 110398f44cb0SIgor Mitsyanko cfg80211_disconnected(vif->netdev, 110498f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING, 110598f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 110698f44cb0SIgor Mitsyanko qtnf_disconnect(vif->wdev.wiphy, ndev, 110798f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING); 110898f44cb0SIgor Mitsyanko break; 110998f44cb0SIgor Mitsyanko } 111098f44cb0SIgor Mitsyanko 111198f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 111298f44cb0SIgor Mitsyanko qtnf_scan_done(mac, true); 111398f44cb0SIgor Mitsyanko } 111498f44cb0SIgor Mitsyanko } 111598f44cb0SIgor Mitsyanko 111698f44cb0SIgor Mitsyanko void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) 111798f44cb0SIgor Mitsyanko { 111898f44cb0SIgor Mitsyanko if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { 111998f44cb0SIgor Mitsyanko switch (vif->sta_state) { 112098f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTING: 112198f44cb0SIgor Mitsyanko cfg80211_connect_result(vif->netdev, 112298f44cb0SIgor Mitsyanko vif->bss_cfg.bssid, NULL, 0, 112398f44cb0SIgor Mitsyanko NULL, 0, 112498f44cb0SIgor Mitsyanko WLAN_STATUS_UNSPECIFIED_FAILURE, 112598f44cb0SIgor Mitsyanko GFP_KERNEL); 112698f44cb0SIgor Mitsyanko break; 112798f44cb0SIgor Mitsyanko case QTNF_STA_CONNECTED: 112898f44cb0SIgor Mitsyanko cfg80211_disconnected(vif->netdev, 112998f44cb0SIgor Mitsyanko WLAN_REASON_DEAUTH_LEAVING, 113098f44cb0SIgor Mitsyanko NULL, 0, 1, GFP_KERNEL); 113198f44cb0SIgor Mitsyanko break; 113298f44cb0SIgor Mitsyanko case QTNF_STA_DISCONNECTED: 113398f44cb0SIgor Mitsyanko break; 113498f44cb0SIgor Mitsyanko } 113598f44cb0SIgor Mitsyanko } 113698f44cb0SIgor Mitsyanko 113798f44cb0SIgor Mitsyanko cfg80211_shutdown_all_interfaces(vif->wdev.wiphy); 113898f44cb0SIgor Mitsyanko vif->sta_state = QTNF_STA_DISCONNECTED; 113998f44cb0SIgor Mitsyanko } 114098f44cb0SIgor Mitsyanko 114198f44cb0SIgor Mitsyanko void qtnf_band_init_rates(struct ieee80211_supported_band *band) 114298f44cb0SIgor Mitsyanko { 114398f44cb0SIgor Mitsyanko switch (band->band) { 114498f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 114598f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_2g; 114698f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_2g); 114798f44cb0SIgor Mitsyanko break; 114898f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 114998f44cb0SIgor Mitsyanko band->bitrates = qtnf_rates_5g; 115098f44cb0SIgor Mitsyanko band->n_bitrates = ARRAY_SIZE(qtnf_rates_5g); 115198f44cb0SIgor Mitsyanko break; 115298f44cb0SIgor Mitsyanko default: 115398f44cb0SIgor Mitsyanko band->bitrates = NULL; 115498f44cb0SIgor Mitsyanko band->n_bitrates = 0; 115598f44cb0SIgor Mitsyanko break; 115698f44cb0SIgor Mitsyanko } 115798f44cb0SIgor Mitsyanko } 1158