diff options
| author | James Morris <james.l.morris@oracle.com> | 2017-11-29 12:47:41 +1100 |
|---|---|---|
| committer | James Morris <james.l.morris@oracle.com> | 2017-11-29 12:47:41 +1100 |
| commit | cf40a76e7d5874bb25f4404eecc58a2e033af885 (patch) | |
| tree | 8fd81cbea03c87b3d41d7ae5b1d11eadd35d6ef5 /drivers/net/wireless/quantenna | |
| parent | ab5348c9c23cd253f5902980d2d8fe067dc24c82 (diff) | |
| parent | 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff) | |
| download | talos-op-linux-cf40a76e7d5874bb25f4404eecc58a2e033af885.tar.gz talos-op-linux-cf40a76e7d5874bb25f4404eecc58a2e033af885.zip | |
Merge tag 'v4.15-rc1' into next-seccomp
Linux 4.15-rc1
Diffstat (limited to 'drivers/net/wireless/quantenna')
16 files changed, 1920 insertions, 782 deletions
diff --git a/drivers/net/wireless/quantenna/qtnfmac/Makefile b/drivers/net/wireless/quantenna/qtnfmac/Makefile index 0d618e5e5f5b..97f760a3d599 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/Makefile +++ b/drivers/net/wireless/quantenna/qtnfmac/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Copyright (c) 2015-2016 Quantenna Communications, Inc. # All rights reserved. @@ -25,7 +26,3 @@ qtnfmac_pearl_pcie-objs += \ pearl/pcie.o qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o - -# - -ccflags-y += -D__CHECK_ENDIAN diff --git a/drivers/net/wireless/quantenna/qtnfmac/bus.h b/drivers/net/wireless/quantenna/qtnfmac/bus.h index dda05003d522..56e5fed92a2a 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/bus.h +++ b/drivers/net/wireless/quantenna/qtnfmac/bus.h @@ -130,7 +130,6 @@ static __always_inline void qtnf_bus_unlock(struct qtnf_bus *bus) /* interface functions from common layer */ -void qtnf_rx_frame(struct device *dev, struct sk_buff *rxp); int qtnf_core_attach(struct qtnf_bus *bus); void qtnf_core_detach(struct qtnf_bus *bus); void qtnf_txflowblock(struct device *dev, bool state); diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c index e3c090008125..6711e7fb6926 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c @@ -73,7 +73,10 @@ qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_AP] = { .tx = BIT(IEEE80211_STYPE_ACTION >> 4), .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4), }, }; @@ -115,6 +118,8 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) vif = qtnf_netdev_get_priv(wdev->netdev); + qtnf_scan_done(vif->mac, true); + if (qtnf_cmd_send_del_intf(vif)) pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, vif->vifid); @@ -131,6 +136,7 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) vif->netdev = NULL; vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; eth_zero_addr(vif->mac_addr); + eth_zero_addr(vif->bssid); return 0; } @@ -199,6 +205,8 @@ err_mac: qtnf_cmd_send_del_intf(vif); err_cmd: vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + eth_zero_addr(vif->mac_addr); + eth_zero_addr(vif->bssid); return ERR_PTR(-EFAULT); } @@ -209,10 +217,10 @@ static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, int ret = 0; if (!info->beacon_ies || !info->beacon_ies_len) { - ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, + ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, NULL, 0); } else { - ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, + ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_IE_SET_BEACON_IES, info->beacon_ies, info->beacon_ies_len); } @@ -222,11 +230,11 @@ static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, if (!info->proberesp_ies || !info->proberesp_ies_len) { ret = qtnf_cmd_send_mgmt_set_appie(vif, - QLINK_MGMT_FRAME_PROBE_RESP, + QLINK_IE_SET_PROBE_RESP_IES, NULL, 0); } else { ret = qtnf_cmd_send_mgmt_set_appie(vif, - QLINK_MGMT_FRAME_PROBE_RESP, + QLINK_IE_SET_PROBE_RESP_IES, info->proberesp_ies, info->proberesp_ies_len); } @@ -236,11 +244,11 @@ static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, if (!info->assocresp_ies || !info->assocresp_ies_len) { ret = qtnf_cmd_send_mgmt_set_appie(vif, - QLINK_MGMT_FRAME_ASSOC_RESP, + QLINK_IE_SET_ASSOC_RESP, NULL, 0); } else { ret = qtnf_cmd_send_mgmt_set_appie(vif, - QLINK_MGMT_FRAME_ASSOC_RESP, + QLINK_IE_SET_ASSOC_RESP, info->assocresp_ies, info->assocresp_ies_len); } @@ -254,11 +262,6 @@ static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); - if (!(vif->bss_status & QTNF_STATE_AP_START)) { - pr_err("VIF%u.%u: not started\n", vif->mac->macid, vif->vifid); - return -EFAULT; - } - return qtnf_mgmt_set_appie(vif, info); } @@ -266,61 +269,13 @@ static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *settings) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); - struct qtnf_bss_config *bss_cfg; int ret; - bss_cfg = &vif->bss_cfg; - - memset(bss_cfg, 0, sizeof(*bss_cfg)); - - bss_cfg->bcn_period = settings->beacon_interval; - bss_cfg->dtim = settings->dtim_period; - bss_cfg->auth_type = settings->auth_type; - bss_cfg->privacy = settings->privacy; - - bss_cfg->ssid_len = settings->ssid_len; - memcpy(&bss_cfg->ssid, settings->ssid, bss_cfg->ssid_len); - - memcpy(&bss_cfg->chandef, &settings->chandef, - sizeof(struct cfg80211_chan_def)); - memcpy(&bss_cfg->crypto, &settings->crypto, - sizeof(struct cfg80211_crypto_settings)); - - ret = qtnf_cmd_send_config_ap(vif); - if (ret) { - pr_err("VIF%u.%u: failed to push config to FW\n", - vif->mac->macid, vif->vifid); - goto out; - } - - if (!(vif->bss_status & QTNF_STATE_AP_CONFIG)) { - pr_err("VIF%u.%u: AP config failed in FW\n", vif->mac->macid, - vif->vifid); - ret = -EFAULT; - goto out; - } - - ret = qtnf_mgmt_set_appie(vif, &settings->beacon); - if (ret) { - pr_err("VIF%u.%u: failed to add IEs to beacon\n", - vif->mac->macid, vif->vifid); - goto out; - } - - ret = qtnf_cmd_send_start_ap(vif); - if (ret) { + ret = qtnf_cmd_send_start_ap(vif, settings); + if (ret) pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, vif->vifid); - goto out; - } - - if (!(vif->bss_status & QTNF_STATE_AP_START)) { - pr_err("VIF%u.%u: FW failed to start AP operation\n", - vif->mac->macid, vif->vifid); - ret = -EFAULT; - } -out: return ret; } @@ -329,12 +284,12 @@ static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); int ret; + qtnf_scan_done(vif->mac, true); + ret = qtnf_cmd_send_stop_ap(vif); if (ret) { pr_err("VIF%u.%u: failed to stop AP operation in FW\n", vif->mac->macid, vif->vifid); - vif->bss_status &= ~QTNF_STATE_AP_START; - vif->bss_status &= ~QTNF_STATE_AP_CONFIG; netif_carrier_off(vif->netdev); } @@ -386,6 +341,13 @@ qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, return; switch (frame_type & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_REASSOC_REQ: + case IEEE80211_STYPE_ASSOC_REQ: + qlink_frame_type = QLINK_MGMT_FRAME_ASSOC_REQ; + break; + case IEEE80211_STYPE_AUTH: + qlink_frame_type = QLINK_MGMT_FRAME_AUTH; + break; case IEEE80211_STYPE_PROBE_REQ: qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ; break; @@ -564,8 +526,6 @@ qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, !qtnf_sta_list_lookup(&vif->sta_list, params->mac)) return 0; - qtnf_scan_done(vif->mac, true); - ret = qtnf_cmd_send_del_sta(vif, params); if (ret) pr_err("VIF%u.%u: failed to delete STA %pM\n", @@ -573,19 +533,32 @@ qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, return ret; } +static void qtnf_scan_timeout(struct timer_list *t) +{ + struct qtnf_wmac *mac = from_timer(mac, t, scan_timeout); + + pr_warn("mac%d scan timed out\n", mac->macid); + qtnf_scan_done(mac, true); +} + static int qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct qtnf_wmac *mac = wiphy_priv(wiphy); - int ret; mac->scan_req = request; - ret = qtnf_cmd_send_scan(mac); - if (ret) + if (qtnf_cmd_send_scan(mac)) { pr_err("MAC%u: failed to start scan\n", mac->macid); + mac->scan_req = NULL; + return -EFAULT; + } - return ret; + mac->scan_timeout.function = qtnf_scan_timeout; + mod_timer(&mac->scan_timeout, + jiffies + QTNF_SCAN_TIMEOUT_SEC * HZ); + + return 0; } static int @@ -593,7 +566,6 @@ qtnf_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); - struct qtnf_bss_config *bss_cfg; int ret; if (vif->wdev.iftype != NL80211_IFTYPE_STATION) @@ -602,38 +574,10 @@ qtnf_connect(struct wiphy *wiphy, struct net_device *dev, if (vif->sta_state != QTNF_STA_DISCONNECTED) return -EBUSY; - bss_cfg = &vif->bss_cfg; - memset(bss_cfg, 0, sizeof(*bss_cfg)); - - bss_cfg->ssid_len = sme->ssid_len; - memcpy(&bss_cfg->ssid, sme->ssid, bss_cfg->ssid_len); - bss_cfg->chandef.chan = sme->channel; - bss_cfg->auth_type = sme->auth_type; - bss_cfg->privacy = sme->privacy; - bss_cfg->mfp = sme->mfp; - - if ((sme->bg_scan_period > 0) && - (sme->bg_scan_period <= QTNF_MAX_BG_SCAN_PERIOD)) - bss_cfg->bg_scan_period = sme->bg_scan_period; - else if (sme->bg_scan_period == -1) - bss_cfg->bg_scan_period = QTNF_DEFAULT_BG_SCAN_PERIOD; - else - bss_cfg->bg_scan_period = 0; /* disabled */ - - bss_cfg->connect_flags = 0; - - if (sme->flags & ASSOC_REQ_DISABLE_HT) - bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; - if (sme->flags & ASSOC_REQ_DISABLE_VHT) - bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; - if (sme->flags & ASSOC_REQ_USE_RRM) - bss_cfg->connect_flags |= QLINK_STA_CONNECT_USE_RRM; - - memcpy(&bss_cfg->crypto, &sme->crypto, sizeof(bss_cfg->crypto)); if (sme->bssid) - ether_addr_copy(bss_cfg->bssid, sme->bssid); + ether_addr_copy(vif->bssid, sme->bssid); else - eth_zero_addr(bss_cfg->bssid); + eth_zero_addr(vif->bssid); ret = qtnf_cmd_send_connect(vif, sme); if (ret) { @@ -677,6 +621,135 @@ qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int +qtnf_dump_survey(struct wiphy *wiphy, struct net_device *dev, + int idx, struct survey_info *survey) +{ + struct qtnf_wmac *mac = wiphy_priv(wiphy); + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_supported_band *sband; + const struct cfg80211_chan_def *chandef = &wdev->chandef; + struct ieee80211_channel *chan; + struct qtnf_chan_stats stats; + struct qtnf_vif *vif; + int ret; + + vif = qtnf_netdev_get_priv(dev); + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = wiphy->bands[NL80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) + return -ENOENT; + + chan = &sband->channels[idx]; + memset(&stats, 0, sizeof(stats)); + + survey->channel = chan; + survey->filled = 0x0; + + if (chandef->chan) { + if (chan->hw_value == chandef->chan->hw_value) + survey->filled = SURVEY_INFO_IN_USE; + } + + ret = qtnf_cmd_get_chan_stats(mac, chan->hw_value, &stats); + switch (ret) { + case 0: + if (unlikely(stats.chan_num != chan->hw_value)) { + pr_err("received stats for channel %d instead of %d\n", + stats.chan_num, chan->hw_value); + ret = -EINVAL; + break; + } + + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_SCAN | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX | + SURVEY_INFO_NOISE_DBM; + + survey->time_scan = stats.cca_try; + survey->time = stats.cca_try; + survey->time_tx = stats.cca_tx; + survey->time_rx = stats.cca_rx; + survey->time_busy = stats.cca_busy; + survey->noise = stats.chan_noise; + break; + case -ENOENT: + pr_debug("no stats for channel %u\n", chan->hw_value); + ret = 0; + break; + default: + pr_debug("failed to get chan(%d) stats from card\n", + chan->hw_value); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + struct net_device *ndev = wdev->netdev; + struct qtnf_vif *vif; + int ret; + + if (!ndev) + return -ENODEV; + + vif = qtnf_netdev_get_priv(wdev->netdev); + + ret = qtnf_cmd_get_channel(vif, chandef); + if (ret) { + pr_err("%s: failed to get channel: %d\n", ndev->name, ret); + goto out; + } + + if (!cfg80211_chandef_valid(chandef)) { + pr_err("%s: bad chan freq1=%u freq2=%u bw=%u\n", ndev->name, + chandef->center_freq1, chandef->center_freq2, + chandef->width); + ret = -ENODATA; + } + +out: + return ret; +} + +static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); + int ret; + + pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name, + params->chandef.chan->hw_value, params->count, + params->radar_required, params->block_tx); + + if (!cfg80211_chandef_valid(¶ms->chandef)) { + pr_err("%s: invalid channel\n", dev->name); + return -EINVAL; + } + + ret = qtnf_cmd_send_chan_switch(vif, params); + if (ret) + pr_warn("%s: failed to switch to channel (%u)\n", + dev->name, params->chandef.chan->hw_value); + + return ret; +} + static struct cfg80211_ops qtn_cfg80211_ops = { .add_virtual_intf = qtnf_add_virtual_intf, .change_virtual_intf = qtnf_change_virtual_intf, @@ -697,100 +770,52 @@ static struct cfg80211_ops qtn_cfg80211_ops = { .set_default_mgmt_key = qtnf_set_default_mgmt_key, .scan = qtnf_scan, .connect = qtnf_connect, - .disconnect = qtnf_disconnect + .disconnect = qtnf_disconnect, + .dump_survey = qtnf_dump_survey, + .get_channel = qtnf_get_channel, + .channel_switch = qtnf_channel_switch }; -static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy, +static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in, struct regulatory_request *req) { - struct qtnf_wmac *mac = wiphy_priv(wiphy); - struct qtnf_bus *bus; - struct qtnf_vif *vif; - struct qtnf_wmac *chan_mac; - int i; + struct qtnf_wmac *mac = wiphy_priv(wiphy_in); + struct qtnf_bus *bus = mac->bus; + struct wiphy *wiphy; + unsigned int mac_idx; enum nl80211_band band; - - bus = mac->bus; + int ret; pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, req->alpha2[0], req->alpha2[1]); - vif = qtnf_mac_get_base_vif(mac); - if (!vif) { - pr_err("MAC%u: primary VIF is not configured\n", mac->macid); - return; - } - - /* ignore non-ISO3166 country codes */ - for (i = 0; i < sizeof(req->alpha2); i++) { - if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { - pr_err("MAC%u: not an ISO3166 code\n", mac->macid); - return; - } - } - if (!strncasecmp(req->alpha2, bus->hw_info.alpha2_code, - sizeof(req->alpha2))) { - pr_warn("MAC%u: unchanged country code\n", mac->macid); - return; - } - - if (qtnf_cmd_send_regulatory_config(mac, req->alpha2)) { - pr_err("MAC%u: failed to configure regulatory\n", mac->macid); + ret = qtnf_cmd_reg_notify(bus, req); + if (ret) { + if (ret != -EOPNOTSUPP && ret != -EALREADY) + pr_err("failed to update reg domain to %c%c\n", + req->alpha2[0], req->alpha2[1]); return; } - for (i = 0; i < bus->hw_info.num_mac; i++) { - chan_mac = bus->mac[i]; - - if (!chan_mac) + for (mac_idx = 0; mac_idx < QTNF_MAX_MAC; ++mac_idx) { + if (!(bus->hw_info.mac_bitmap & (1 << mac_idx))) continue; - if (!(bus->hw_info.mac_bitmap & BIT(i))) - continue; + mac = bus->mac[mac_idx]; + wiphy = priv_to_wiphy(mac); for (band = 0; band < NUM_NL80211_BANDS; ++band) { if (!wiphy->bands[band]) continue; - if (qtnf_cmd_get_mac_chan_info(chan_mac, - wiphy->bands[band])) { - pr_err("MAC%u: can't get channel info\n", - chan_mac->macid); - qtnf_core_detach(bus); - - return; - } + ret = qtnf_cmd_band_info_get(mac, wiphy->bands[band]); + if (ret) + pr_err("failed to get chan info for mac %u band %u\n", + mac_idx, band); } } } -void qtnf_band_setup_htvht_caps(struct qtnf_mac_info *macinfo, - struct ieee80211_supported_band *band) -{ - struct ieee80211_sta_ht_cap *ht_cap; - struct ieee80211_sta_vht_cap *vht_cap; - - ht_cap = &band->ht_cap; - ht_cap->ht_supported = true; - memcpy(&ht_cap->cap, &macinfo->ht_cap.cap_info, - sizeof(u16)); - ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; - memcpy(&ht_cap->mcs, &macinfo->ht_cap.mcs, - sizeof(ht_cap->mcs)); - - if (macinfo->phymode_cap & QLINK_PHYMODE_AC) { - vht_cap = &band->vht_cap; - vht_cap->vht_supported = true; - memcpy(&vht_cap->cap, - &macinfo->vht_cap.vht_cap_info, sizeof(u32)); - /* Update MCS support for VHT */ - memcpy(&vht_cap->vht_mcs, - &macinfo->vht_cap.supp_mcs, - sizeof(struct ieee80211_vht_mcs_info)); - } -} - struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) { struct wiphy *wiphy; @@ -844,18 +869,13 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) } iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); - if (!iface_comb) { - ret = -ENOMEM; - goto out; - } + if (!iface_comb) + return -ENOMEM; ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); if (ret) goto out; - pr_info("MAC%u: phymode=%#x radar=%#x\n", mac->macid, - mac->macinfo.phymode_cap, mac->macinfo.radar_detect_widths); - wiphy->frag_threshold = mac->macinfo.frag_thr; wiphy->rts_threshold = mac->macinfo.rts_thr; wiphy->retry_short = mac->macinfo.sretry_limit; @@ -869,6 +889,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) wiphy->iface_combinations = iface_comb; wiphy->n_iface_combinations = 1; + wiphy->max_num_csa_counters = 2; /* Initialize cipher suits */ wiphy->cipher_suites = qtnf_cipher_suites; @@ -876,7 +897,8 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | - WIPHY_FLAG_AP_UAPSD; + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; @@ -885,25 +907,35 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) wiphy->available_antennas_rx = mac->macinfo.num_rx_chain; wiphy->max_ap_assoc_sta = mac->macinfo.max_ap_assoc_sta; + wiphy->ht_capa_mod_mask = &mac->macinfo.ht_cap_mod_mask; + wiphy->vht_capa_mod_mask = &mac->macinfo.vht_cap_mod_mask; ether_addr_copy(wiphy->perm_addr, mac->macaddr); - if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) { - pr_debug("device supports REG_UPDATE\n"); + if (hw_info->hw_capab & QLINK_HW_CAPAB_STA_INACT_TIMEOUT) + wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; + + if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) { + wiphy->regulatory_flags |= REGULATORY_STRICT_REG | + REGULATORY_CUSTOM_REG; wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; - pr_debug("hint regulatory about EP region: %c%c\n", - hw_info->alpha2_code[0], - hw_info->alpha2_code[1]); - regulatory_hint(wiphy, hw_info->alpha2_code); + wiphy_apply_custom_regulatory(wiphy, hw_info->rd); } else { - pr_debug("device doesn't support REG_UPDATE\n"); wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; } ret = wiphy_register(wiphy); + if (ret < 0) + goto out; + + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + ret = regulatory_set_wiphy_regd(wiphy, hw_info->rd); + else if (isalpha(hw_info->rd->alpha2[0]) && + isalpha(hw_info->rd->alpha2[1])) + ret = regulatory_hint(wiphy, hw_info->rd->alpha2); out: - if (ret < 0) { + if (ret) { kfree(iface_comb); return ret; } @@ -930,7 +962,7 @@ void qtnf_virtual_intf_cleanup(struct net_device *ndev) break; case QTNF_STA_CONNECTING: cfg80211_connect_result(vif->netdev, - vif->bss_cfg.bssid, NULL, 0, + vif->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); @@ -947,8 +979,9 @@ void qtnf_virtual_intf_cleanup(struct net_device *ndev) } vif->sta_state = QTNF_STA_DISCONNECTED; - qtnf_scan_done(mac, true); } + + qtnf_scan_done(mac, true); } void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) @@ -957,7 +990,7 @@ void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) switch (vif->sta_state) { case QTNF_STA_CONNECTING: cfg80211_connect_result(vif->netdev, - vif->bss_cfg.bssid, NULL, 0, + vif->bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h index 5bd33124a7c8..66db26613b1f 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h +++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.h @@ -34,10 +34,17 @@ static inline void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted) .aborted = aborted, }; + if (timer_pending(&mac->scan_timeout)) + del_timer_sync(&mac->scan_timeout); + + mutex_lock(&mac->mac_lock); + if (mac->scan_req) { cfg80211_scan_done(mac->scan_req, &info); mac->scan_req = NULL; } + + mutex_unlock(&mac->mac_lock); } #endif /* _QTN_FMAC_CFG80211_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index b39dbc3d3c1f..8bc8dd637315 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -147,128 +147,143 @@ static struct sk_buff *qtnf_cmd_alloc_new_cmdskb(u8 macid, u8 vifid, u16 cmd_no, return cmd_skb; } -int qtnf_cmd_send_start_ap(struct qtnf_vif *vif) +static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type, + const u8 *buf, size_t len) { - struct sk_buff *cmd_skb; - u16 res_code = QLINK_CMD_RESULT_OK; - int ret; - - cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, - QLINK_CMD_START_AP, - sizeof(struct qlink_cmd)); - if (unlikely(!cmd_skb)) - return -ENOMEM; - - qtnf_bus_lock(vif->mac->bus); - - ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); - - if (unlikely(ret)) - goto out; - - if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { - pr_err("VIF%u.%u: CMD failed: %u\n", vif->mac->macid, - vif->vifid, res_code); - ret = -EFAULT; - goto out; - } + struct qlink_tlv_ie_set *tlv; - vif->bss_status |= QTNF_STATE_AP_START; - netif_carrier_on(vif->netdev); + tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) + len); + tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_IE_SET); + tlv->hdr.len = cpu_to_le16(len + sizeof(*tlv) - sizeof(tlv->hdr)); + tlv->type = frame_type; + tlv->flags = 0; -out: - qtnf_bus_unlock(vif->mac->bus); - return ret; + if (len && buf) + memcpy(tlv->ie_data, buf, len); } -int qtnf_cmd_send_regulatory_config(struct qtnf_wmac *mac, const char *alpha2) +static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif, + const struct cfg80211_ap_settings *s) { - struct sk_buff *cmd_skb; - u16 res_code = QLINK_CMD_RESULT_OK; - int ret; - - cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, - QLINK_CMD_REG_REGION, - sizeof(struct qlink_cmd)); - if (unlikely(!cmd_skb)) - return -ENOMEM; - - qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_COUNTRY, alpha2, - QTNF_MAX_ALPHA_LEN); - - ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); - - if (unlikely(ret)) - goto out; - - if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { - pr_err("MAC%u: CMD failed: %u\n", mac->macid, res_code); - ret = -EFAULT; - goto out; + unsigned int len = sizeof(struct qlink_cmd_start_ap); + + len += s->ssid_len; + len += s->beacon.head_len; + len += s->beacon.tail_len; + len += s->beacon.beacon_ies_len; + len += s->beacon.proberesp_ies_len; + len += s->beacon.assocresp_ies_len; + len += s->beacon.probe_resp_len; + + if (cfg80211_chandef_valid(&s->chandef)) + len += sizeof(struct qlink_tlv_chandef); + + if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) { + pr_err("VIF%u.%u: can not fit AP settings: %u\n", + vif->mac->macid, vif->vifid, len); + return false; } - memcpy(mac->bus->hw_info.alpha2_code, alpha2, - sizeof(mac->bus->hw_info.alpha2_code)); -out: - return ret; + return true; } -int qtnf_cmd_send_config_ap(struct qtnf_vif *vif) +int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, + const struct cfg80211_ap_settings *s) { struct sk_buff *cmd_skb; - struct qtnf_bss_config *bss_cfg = &vif->bss_cfg; - struct cfg80211_chan_def *chandef = &bss_cfg->chandef; - struct qlink_tlv_channel *qchan; - struct qlink_auth_encr aen; + struct qlink_cmd_start_ap *cmd; + struct qlink_auth_encr *aen; u16 res_code = QLINK_CMD_RESULT_OK; int ret; int i; + if (!qtnf_cmd_start_ap_can_fit(vif, s)) + return -E2BIG; + cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, - QLINK_CMD_CONFIG_AP, - sizeof(struct qlink_cmd)); + QLINK_CMD_START_AP, + sizeof(*cmd)); if (unlikely(!cmd_skb)) return -ENOMEM; - qtnf_bus_lock(vif->mac->bus); - - qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, bss_cfg->ssid, - bss_cfg->ssid_len); - qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_BCN_PERIOD, - bss_cfg->bcn_period); - qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_DTIM, bss_cfg->dtim); - - qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); - qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); - qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - - sizeof(struct qlink_tlv_hdr)); - qchan->hw_value = cpu_to_le16( - ieee80211_frequency_to_channel(chandef->chan->center_freq)); - - memset(&aen, 0, sizeof(aen)); - aen.auth_type = bss_cfg->auth_type; - aen.privacy = !!bss_cfg->privacy; - aen.mfp = bss_cfg->mfp; - aen.wpa_versions = cpu_to_le32(bss_cfg->crypto.wpa_versions); - aen.cipher_group = cpu_to_le32(bss_cfg->crypto.cipher_group); - aen.n_ciphers_pairwise = cpu_to_le32( - bss_cfg->crypto.n_ciphers_pairwise); + cmd = (struct qlink_cmd_start_ap *)cmd_skb->data; + cmd->dtim_period = s->dtim_period; + cmd->beacon_interval = cpu_to_le16(s->beacon_interval); + cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid); + cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout); + cmd->smps_mode = s->smps_mode; + cmd->p2p_ctwindow = s->p2p_ctwindow; + cmd->p2p_opp_ps = s->p2p_opp_ps; + cmd->pbss = s->pbss; + cmd->ht_required = s->ht_required; + cmd->vht_required = s->vht_required; + + aen = &cmd->aen; + aen->auth_type = s->auth_type; + aen->privacy = !!s->privacy; + aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions); + aen->cipher_group = cpu_to_le32(s->crypto.cipher_group); + aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise); for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) - aen.ciphers_pairwise[i] = cpu_to_le32( - bss_cfg->crypto.ciphers_pairwise[i]); - aen.n_akm_suites = cpu_to_le32( - bss_cfg->crypto.n_akm_suites); + aen->ciphers_pairwise[i] = + cpu_to_le32(s->crypto.ciphers_pairwise[i]); + aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites); for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) - aen.akm_suites[i] = cpu_to_le32( - bss_cfg->crypto.akm_suites[i]); - aen.control_port = bss_cfg->crypto.control_port; - aen.control_port_no_encrypt = - bss_cfg->crypto.control_port_no_encrypt; - aen.control_port_ethertype = cpu_to_le16(be16_to_cpu( - bss_cfg->crypto.control_port_ethertype)); + aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]); + aen->control_port = s->crypto.control_port; + aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt; + aen->control_port_ethertype = + cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype)); + + if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN) + qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid, + s->ssid_len); + + if (cfg80211_chandef_valid(&s->chandef)) { + struct qlink_tlv_chandef *chtlv = + (struct qlink_tlv_chandef *)skb_put(cmd_skb, + sizeof(*chtlv)); + + chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF); + chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) - + sizeof(chtlv->hdr)); + qlink_chandef_cfg2q(&s->chandef, &chtlv->chan); + } + + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_HEAD, + s->beacon.head, s->beacon.head_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_TAIL, + s->beacon.tail, s->beacon.tail_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_IES, + s->beacon.beacon_ies, s->beacon.beacon_ies_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP, + s->beacon.probe_resp, s->beacon.probe_resp_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP_IES, + s->beacon.proberesp_ies, + s->beacon.proberesp_ies_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_RESP, + s->beacon.assocresp_ies, + s->beacon.assocresp_ies_len); + + if (s->ht_cap) { + struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) + skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->ht_cap)); + + tlv->type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + tlv->len = cpu_to_le16(sizeof(*s->ht_cap)); + memcpy(tlv->val, s->ht_cap, sizeof(*s->ht_cap)); + } + + if (s->vht_cap) { + struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) + skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->vht_cap)); + + tlv->type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + tlv->len = cpu_to_le16(sizeof(*s->vht_cap)); + memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap)); + } - qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_CRYPTO, (u8 *)&aen, - sizeof(aen)); + qtnf_bus_lock(vif->mac->bus); ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); @@ -282,7 +297,7 @@ int qtnf_cmd_send_config_ap(struct qtnf_vif *vif) goto out; } - vif->bss_status |= QTNF_STATE_AP_CONFIG; + netif_carrier_on(vif->netdev); out: qtnf_bus_unlock(vif->mac->bus); @@ -315,9 +330,6 @@ int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif) goto out; } - vif->bss_status &= ~QTNF_STATE_AP_START; - vif->bss_status &= ~QTNF_STATE_AP_CONFIG; - netif_carrier_off(vif->netdev); out: @@ -412,11 +424,10 @@ int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type, const u8 *buf, size_t len) { struct sk_buff *cmd_skb; - struct qlink_cmd_mgmt_append_ie *cmd; u16 res_code = QLINK_CMD_RESULT_OK; int ret; - if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) { + if (len > QTNF_MAX_CMD_BUF_SIZE) { pr_warn("VIF%u.%u: %u frame is too big: %zu\n", vif->mac->macid, vif->vifid, frame_type, len); return -E2BIG; @@ -424,21 +435,13 @@ int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type, cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, QLINK_CMD_MGMT_SET_APPIE, - sizeof(*cmd)); + sizeof(struct qlink_cmd)); if (unlikely(!cmd_skb)) return -ENOMEM; - qtnf_bus_lock(vif->mac->bus); - - cmd = (struct qlink_cmd_mgmt_append_ie *)cmd_skb->data; - cmd->type = frame_type; - cmd->flags = 0; + qtnf_cmd_tlv_ie_set_add(cmd_skb, frame_type, buf, len); - /* If len == 0 then IE buf for specified frame type - * should be cleared on EP. - */ - if (len && buf) - qtnf_cmd_skb_put_buffer(cmd_skb, buf, len); + qtnf_bus_lock(vif->mac->bus); ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); @@ -848,26 +851,170 @@ out: return ret; } +static u32 qtnf_cmd_resp_reg_rule_flags_parse(u32 qflags) +{ + u32 flags = 0; + + if (qflags & QLINK_RRF_NO_OFDM) + flags |= NL80211_RRF_NO_OFDM; + + if (qflags & QLINK_RRF_NO_CCK) + flags |= NL80211_RRF_NO_CCK; + + if (qflags & QLINK_RRF_NO_INDOOR) + flags |= NL80211_RRF_NO_INDOOR; + + if (qflags & QLINK_RRF_NO_OUTDOOR) + flags |= NL80211_RRF_NO_OUTDOOR; + + if (qflags & QLINK_RRF_DFS) + flags |= NL80211_RRF_DFS; + + if (qflags & QLINK_RRF_PTP_ONLY) + flags |= NL80211_RRF_PTP_ONLY; + + if (qflags & QLINK_RRF_PTMP_ONLY) + flags |= NL80211_RRF_PTMP_ONLY; + + if (qflags & QLINK_RRF_NO_IR) + flags |= NL80211_RRF_NO_IR; + + if (qflags & QLINK_RRF_AUTO_BW) + flags |= NL80211_RRF_AUTO_BW; + + if (qflags & QLINK_RRF_IR_CONCURRENT) + flags |= NL80211_RRF_IR_CONCURRENT; + + if (qflags & QLINK_RRF_NO_HT40MINUS) + flags |= NL80211_RRF_NO_HT40MINUS; + + if (qflags & QLINK_RRF_NO_HT40PLUS) + flags |= NL80211_RRF_NO_HT40PLUS; + + if (qflags & QLINK_RRF_NO_80MHZ) + flags |= NL80211_RRF_NO_80MHZ; + + if (qflags & QLINK_RRF_NO_160MHZ) + flags |= NL80211_RRF_NO_160MHZ; + + return flags; +} + static int qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, - const struct qlink_resp_get_hw_info *resp) + const struct qlink_resp_get_hw_info *resp, + size_t info_len) { struct qtnf_hw_info *hwinfo = &bus->hw_info; + const struct qlink_tlv_hdr *tlv; + const struct qlink_tlv_reg_rule *tlv_rule; + struct ieee80211_reg_rule *rule; + u16 tlv_type; + u16 tlv_value_len; + unsigned int rule_idx = 0; + + if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) + return -E2BIG; + + hwinfo->rd = kzalloc(sizeof(*hwinfo->rd) + + sizeof(struct ieee80211_reg_rule) + * resp->n_reg_rules, GFP_KERNEL); + + if (!hwinfo->rd) + return -ENOMEM; hwinfo->num_mac = resp->num_mac; hwinfo->mac_bitmap = resp->mac_bitmap; hwinfo->fw_ver = le32_to_cpu(resp->fw_ver); hwinfo->ql_proto_ver = le16_to_cpu(resp->ql_proto_ver); - memcpy(hwinfo->alpha2_code, resp->alpha2_code, - sizeof(hwinfo->alpha2_code)); hwinfo->total_tx_chain = resp->total_tx_chain; hwinfo->total_rx_chain = resp->total_rx_chain; hwinfo->hw_capab = le32_to_cpu(resp->hw_capab); + hwinfo->rd->n_reg_rules = resp->n_reg_rules; + hwinfo->rd->alpha2[0] = resp->alpha2[0]; + hwinfo->rd->alpha2[1] = resp->alpha2[1]; + + switch (resp->dfs_region) { + case QLINK_DFS_FCC: + hwinfo->rd->dfs_region = NL80211_DFS_FCC; + break; + case QLINK_DFS_ETSI: + hwinfo->rd->dfs_region = NL80211_DFS_ETSI; + break; + case QLINK_DFS_JP: + hwinfo->rd->dfs_region = NL80211_DFS_JP; + break; + case QLINK_DFS_UNSET: + default: + hwinfo->rd->dfs_region = NL80211_DFS_UNSET; + break; + } + + tlv = (const struct qlink_tlv_hdr *)resp->info; + + while (info_len >= sizeof(*tlv)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_value_len = le16_to_cpu(tlv->len); - pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u\n", + if (tlv_value_len + sizeof(*tlv) > info_len) { + pr_warn("malformed TLV 0x%.2X; LEN: %u\n", + tlv_type, tlv_value_len); + return -EINVAL; + } + + switch (tlv_type) { + case QTN_TLV_ID_REG_RULE: + if (rule_idx >= resp->n_reg_rules) { + pr_warn("unexpected number of rules: %u\n", + resp->n_reg_rules); + return -EINVAL; + } + + if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) { + pr_warn("malformed TLV 0x%.2X; LEN: %u\n", + tlv_type, tlv_value_len); + return -EINVAL; + } + + tlv_rule = (const struct qlink_tlv_reg_rule *)tlv; + rule = &hwinfo->rd->reg_rules[rule_idx++]; + + rule->freq_range.start_freq_khz = + le32_to_cpu(tlv_rule->start_freq_khz); + rule->freq_range.end_freq_khz = + le32_to_cpu(tlv_rule->end_freq_khz); + rule->freq_range.max_bandwidth_khz = + le32_to_cpu(tlv_rule->max_bandwidth_khz); + rule->power_rule.max_antenna_gain = + le32_to_cpu(tlv_rule->max_antenna_gain); + rule->power_rule.max_eirp = + le32_to_cpu(tlv_rule->max_eirp); + rule->dfs_cac_ms = + le32_to_cpu(tlv_rule->dfs_cac_ms); + rule->flags = qtnf_cmd_resp_reg_rule_flags_parse( + le32_to_cpu(tlv_rule->flags)); + break; + default: + break; + } + + info_len -= tlv_value_len + sizeof(*tlv); + tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); + } + + if (rule_idx != resp->n_reg_rules) { + pr_warn("unexpected number of rules: expected %u got %u\n", + resp->n_reg_rules, rule_idx); + kfree(hwinfo->rd); + hwinfo->rd = NULL; + return -EINVAL; + } + + pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n", hwinfo->fw_ver, hwinfo->mac_bitmap, - hwinfo->alpha2_code[0], hwinfo->alpha2_code[1], - hwinfo->total_tx_chain, hwinfo->total_rx_chain); + hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1], + hwinfo->total_tx_chain, hwinfo->total_rx_chain, + hwinfo->hw_capab); return 0; } @@ -878,7 +1025,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, struct ieee80211_iface_limit *limits = NULL; const struct qlink_iface_limit *limit_record; size_t record_count = 0, rec = 0; - u16 tlv_type, tlv_value_len, mask; + u16 tlv_type, tlv_value_len; struct qlink_iface_comb_num *comb; size_t tlv_full_len; const struct qlink_tlv_hdr *tlv; @@ -931,10 +1078,12 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, limit_record = (void *)tlv->val; limits[rec].max = le16_to_cpu(limit_record->max_num); - mask = le16_to_cpu(limit_record->type_mask); - limits[rec].types = qlink_iface_type_mask_to_nl(mask); - /* only AP and STA modes are supported */ + limits[rec].types = qlink_iface_type_to_nl_mask( + le16_to_cpu(limit_record->type)); + + /* supported modes: STA, AP */ limits[rec].types &= BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_AP_VLAN) | BIT(NL80211_IFTYPE_STATION); pr_debug("MAC%u: MAX: %u; TYPES: %.4X\n", mac->macid, @@ -946,6 +1095,7 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, default: break; } + tlv_buf_size -= tlv_full_len; tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } @@ -975,7 +1125,6 @@ qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, mac_info = &mac->macinfo; mac_info->bands_cap = resp_info->bands_cap; - mac_info->phymode_cap = resp_info->phymode_cap; memcpy(&mac_info->dev_mac, &resp_info->dev_mac, sizeof(mac_info->dev_mac)); @@ -995,32 +1144,74 @@ qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, qlink_chan_width_mask_to_nl(le16_to_cpu( resp_info->radar_detect_widths)); - memcpy(&mac_info->ht_cap, &resp_info->ht_cap, sizeof(mac_info->ht_cap)); - memcpy(&mac_info->vht_cap, &resp_info->vht_cap, - sizeof(mac_info->vht_cap)); + memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask, + sizeof(mac_info->ht_cap_mod_mask)); + memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask, + sizeof(mac_info->vht_cap_mod_mask)); +} + +static void qtnf_cmd_resp_band_fill_htcap(const u8 *info, + struct ieee80211_sta_ht_cap *bcap) +{ + const struct ieee80211_ht_cap *ht_cap = + (const struct ieee80211_ht_cap *)info; + + bcap->ht_supported = true; + bcap->cap = le16_to_cpu(ht_cap->cap_info); + bcap->ampdu_factor = + ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR; + bcap->ampdu_density = + (ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; + memcpy(&bcap->mcs, &ht_cap->mcs, sizeof(bcap->mcs)); +} + +static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info, + struct ieee80211_sta_vht_cap *bcap) +{ + const struct ieee80211_vht_cap *vht_cap = + (const struct ieee80211_vht_cap *)info; + + bcap->vht_supported = true; + bcap->cap = le32_to_cpu(vht_cap->vht_cap_info); + memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs)); } static int -qtnf_cmd_resp_fill_channels_info(struct ieee80211_supported_band *band, - struct qlink_resp_get_chan_info *resp, - size_t payload_len) +qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, + struct qlink_resp_band_info_get *resp, + size_t payload_len) { u16 tlv_type; size_t tlv_len; + size_t tlv_dlen; const struct qlink_tlv_hdr *tlv; const struct qlink_tlv_channel *qchan; struct ieee80211_channel *chan; unsigned int chidx = 0; u32 qflags; - kfree(band->channels); - band->channels = NULL; + memset(&band->ht_cap, 0, sizeof(band->ht_cap)); + memset(&band->vht_cap, 0, sizeof(band->vht_cap)); + + if (band->channels) { + if (band->n_channels == resp->num_chans) { + memset(band->channels, 0, + sizeof(*band->channels) * band->n_channels); + } else { + kfree(band->channels); + band->n_channels = 0; + band->channels = NULL; + } + } band->n_channels = resp->num_chans; if (band->n_channels == 0) return 0; - band->channels = kcalloc(band->n_channels, sizeof(*chan), GFP_KERNEL); + if (!band->channels) + band->channels = kcalloc(band->n_channels, sizeof(*chan), + GFP_KERNEL); if (!band->channels) { band->n_channels = 0; return -ENOMEM; @@ -1030,7 +1221,8 @@ qtnf_cmd_resp_fill_channels_info(struct ieee80211_supported_band *band, while (payload_len >= sizeof(*tlv)) { tlv_type = le16_to_cpu(tlv->type); - tlv_len = le16_to_cpu(tlv->len) + sizeof(*tlv); + tlv_dlen = le16_to_cpu(tlv->len); + tlv_len = tlv_dlen + sizeof(*tlv); if (tlv_len > payload_len) { pr_warn("malformed TLV 0x%.2X; LEN: %zu\n", @@ -1116,13 +1308,32 @@ qtnf_cmd_resp_fill_channels_info(struct ieee80211_supported_band *band, chan->hw_value, chan->flags, chan->max_power, chan->max_reg_power); break; + case WLAN_EID_HT_CAPABILITY: + if (unlikely(tlv_dlen != + sizeof(struct ieee80211_ht_cap))) { + pr_err("bad HTCAP TLV len %zu\n", tlv_dlen); + goto error_ret; + } + + qtnf_cmd_resp_band_fill_htcap(tlv->val, &band->ht_cap); + break; + case WLAN_EID_VHT_CAPABILITY: + if (unlikely(tlv_dlen != + sizeof(struct ieee80211_vht_cap))) { + pr_err("bad VHTCAP TLV len %zu\n", tlv_dlen); + goto error_ret; + } + + qtnf_cmd_resp_band_fill_vhtcap(tlv->val, + &band->vht_cap); + break; default: pr_warn("unknown TLV type: %#x\n", tlv_type); break; } payload_len -= tlv_len; - tlv = (struct qlink_tlv_hdr *)((u8 *)tlv + tlv_len); + tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_dlen); } if (payload_len) { @@ -1212,6 +1423,62 @@ static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac, return 0; } +static int +qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats, + const u8 *payload, size_t payload_len) +{ + struct qlink_chan_stats *qlink_stats; + const struct qlink_tlv_hdr *tlv; + size_t tlv_full_len; + u16 tlv_value_len; + u16 tlv_type; + + tlv = (struct qlink_tlv_hdr *)payload; + while (payload_len >= sizeof(struct qlink_tlv_hdr)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_value_len = le16_to_cpu(tlv->len); + tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); + if (tlv_full_len > payload_len) { + pr_warn("malformed TLV 0x%.2X; LEN: %u\n", + tlv_type, tlv_value_len); + return -EINVAL; + } + switch (tlv_type) { + case QTN_TLV_ID_CHANNEL_STATS: + if (unlikely(tlv_value_len != sizeof(*qlink_stats))) { + pr_err("invalid CHANNEL_STATS entry size\n"); + return -EINVAL; + } + + qlink_stats = (void *)tlv->val; + + stats->chan_num = le32_to_cpu(qlink_stats->chan_num); + stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx); + stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx); + stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy); + stats->cca_try = le32_to_cpu(qlink_stats->cca_try); + stats->chan_noise = qlink_stats->chan_noise; + + pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n", + stats->chan_num, stats->cca_try, + stats->cca_busy, stats->chan_noise); + break; + default: + pr_warn("Unknown TLV type: %#x\n", + le16_to_cpu(tlv->type)); + } + payload_len -= tlv_full_len; + tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); + } + + if (payload_len) { + pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len); + return -EINVAL; + } + + return 0; +} + int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) { struct sk_buff *cmd_skb, *resp_skb = NULL; @@ -1256,6 +1523,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) const struct qlink_resp_get_hw_info *resp; u16 res_code = QLINK_CMD_RESULT_OK; int ret = 0; + size_t info_len; cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, QLINK_CMD_GET_HW_INFO, @@ -1266,7 +1534,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) qtnf_bus_lock(bus); ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, - sizeof(*resp), NULL); + sizeof(*resp), &info_len); if (unlikely(ret)) goto out; @@ -1278,7 +1546,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) } resp = (const struct qlink_resp_get_hw_info *)resp_skb->data; - ret = qtnf_cmd_resp_proc_hw_info(bus, resp); + ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len); out: qtnf_bus_unlock(bus); @@ -1287,13 +1555,13 @@ out: return ret; } -int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, - struct ieee80211_supported_band *band) +int qtnf_cmd_band_info_get(struct qtnf_wmac *mac, + struct ieee80211_supported_band *band) { struct sk_buff *cmd_skb, *resp_skb = NULL; size_t info_len; - struct qlink_cmd_chans_info_get *cmd; - struct qlink_resp_get_chan_info *resp; + struct qlink_cmd_band_info_get *cmd; + struct qlink_resp_band_info_get *resp; u16 res_code = QLINK_CMD_RESULT_OK; int ret = 0; u8 qband; @@ -1313,13 +1581,16 @@ int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, } cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, - QLINK_CMD_CHANS_INFO_GET, + QLINK_CMD_BAND_INFO_GET, sizeof(*cmd)); if (!cmd_skb) return -ENOMEM; - cmd = (struct qlink_cmd_chans_info_get *)cmd_skb->data; + cmd = (struct qlink_cmd_band_info_get *)cmd_skb->data; cmd->band = qband; + + qtnf_bus_lock(mac->bus); + ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, sizeof(*resp), &info_len); @@ -1332,7 +1603,7 @@ int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, goto out; } - resp = (struct qlink_resp_get_chan_info *)resp_skb->data; + resp = (struct qlink_resp_band_info_get *)resp_skb->data; if (resp->band != qband) { pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid, resp->band, qband); @@ -1340,9 +1611,10 @@ int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, goto out; } - ret = qtnf_cmd_resp_fill_channels_info(band, resp, info_len); + ret = qtnf_cmd_resp_fill_band_info(band, resp, info_len); out: + qtnf_bus_unlock(mac->bus); consume_skb(resp_skb); return ret; @@ -1676,10 +1948,27 @@ int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, cmd = (struct qlink_cmd_change_sta *)cmd_skb->data; ether_addr_copy(cmd->sta_addr, mac); - cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( - params->sta_flags_mask)); - cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( - params->sta_flags_set)); + + switch (vif->wdev.iftype) { + case NL80211_IFTYPE_AP: + cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP); + cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_mask)); + cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_set)); + break; + case NL80211_IFTYPE_STATION: + cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION); + cmd->sta_flags_mask = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_mask)); + cmd->sta_flags_set = cpu_to_le32(qtnf_encode_sta_flags( + params->sta_flags_set)); + break; + default: + pr_err("unsupported iftype %d\n", vif->wdev.iftype); + ret = -EINVAL; + goto out; + } ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); if (unlikely(ret)) @@ -1739,17 +2028,36 @@ out: return ret; } +static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb, + const struct ieee80211_channel *sc) +{ + struct qlink_tlv_channel *qchan; + u32 flags = 0; + + qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); + qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); + qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - sizeof(qchan->hdr)); + qchan->center_freq = cpu_to_le16(sc->center_freq); + qchan->hw_value = cpu_to_le16(sc->hw_value); + + if (sc->flags & IEEE80211_CHAN_NO_IR) + flags |= QLINK_CHAN_NO_IR; + + if (sc->flags & IEEE80211_CHAN_RADAR) + flags |= QLINK_CHAN_RADAR; + + qchan->flags = cpu_to_le32(flags); +} + int qtnf_cmd_send_scan(struct qtnf_wmac *mac) { struct sk_buff *cmd_skb; u16 res_code = QLINK_CMD_RESULT_OK; struct ieee80211_channel *sc; struct cfg80211_scan_request *scan_req = mac->scan_req; - struct qlink_tlv_channel *qchan; int n_channels; int count = 0; int ret; - u32 flags; if (scan_req->n_ssids > QTNF_MAX_SSID_LIST_LENGTH) { pr_err("MAC%u: too many SSIDs in scan request\n", mac->macid); @@ -1774,9 +2082,8 @@ int qtnf_cmd_send_scan(struct qtnf_wmac *mac) } if (scan_req->ie_len != 0) - qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_IE_SET, - scan_req->ie, - scan_req->ie_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_REQ, + scan_req->ie, scan_req->ie_len); if (scan_req->n_channels) { n_channels = scan_req->n_channels; @@ -1792,22 +2099,8 @@ int qtnf_cmd_send_scan(struct qtnf_wmac *mac) pr_debug("MAC%u: scan chan=%d, freq=%d, flags=%#x\n", mac->macid, sc->hw_value, sc->center_freq, sc->flags); - qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); - flags = 0; - qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); - qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - - sizeof(struct qlink_tlv_hdr)); - qchan->center_freq = cpu_to_le16(sc->center_freq); - qchan->hw_value = cpu_to_le16(sc->hw_value); - - if (sc->flags & IEEE80211_CHAN_NO_IR) - flags |= QLINK_CHAN_NO_IR; - - if (sc->flags & IEEE80211_CHAN_RADAR) - flags |= QLINK_CHAN_RADAR; - - qchan->flags = cpu_to_le32(flags); + qtnf_cmd_channel_tlv_add(cmd_skb, sc); n_channels--; count++; } @@ -1835,11 +2128,11 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif, { struct sk_buff *cmd_skb; struct qlink_cmd_connect *cmd; - struct qtnf_bss_config *bss_cfg = &vif->bss_cfg; - struct qlink_auth_encr aen; + struct qlink_auth_encr *aen; u16 res_code = QLINK_CMD_RESULT_OK; int ret; int i; + u32 connect_flags = 0; cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, QLINK_CMD_CONNECT, @@ -1847,51 +2140,78 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif, if (unlikely(!cmd_skb)) return -ENOMEM; - qtnf_bus_lock(vif->mac->bus); - cmd = (struct qlink_cmd_connect *)cmd_skb->data; - ether_addr_copy(cmd->bssid, bss_cfg->bssid); + ether_addr_copy(cmd->bssid, vif->bssid); - if (bss_cfg->chandef.chan) - cmd->freq = cpu_to_le16(bss_cfg->chandef.chan->center_freq); + if (sme->bssid_hint) + ether_addr_copy(cmd->bssid_hint, sme->bssid_hint); + else + eth_zero_addr(cmd->bssid_hint); - cmd->bg_scan_period = cpu_to_le16(bss_cfg->bg_scan_period); + if (sme->prev_bssid) + ether_addr_copy(cmd->prev_bssid, sme->prev_bssid); + else + eth_zero_addr(cmd->prev_bssid); - memset(&aen, 0, sizeof(aen)); - aen.auth_type = bss_cfg->auth_type; - aen.privacy = !!bss_cfg->privacy; - aen.mfp = bss_cfg->mfp; - aen.wpa_versions = cpu_to_le32(bss_cfg->crypto.wpa_versions); - aen.cipher_group = cpu_to_le32(bss_cfg->crypto.cipher_group); - aen.n_ciphers_pairwise = cpu_to_le32( - bss_cfg->crypto.n_ciphers_pairwise); + if ((sme->bg_scan_period > 0) && + (sme->bg_scan_period <= QTNF_MAX_BG_SCAN_PERIOD)) + cmd->bg_scan_period = cpu_to_le16(sme->bg_scan_period); + else if (sme->bg_scan_period == -1) + cmd->bg_scan_period = cpu_to_le16(QTNF_DEFAULT_BG_SCAN_PERIOD); + else + cmd->bg_scan_period = 0; /* disabled */ + + if (sme->flags & ASSOC_REQ_DISABLE_HT) + connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; + if (sme->flags & ASSOC_REQ_DISABLE_VHT) + connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; + if (sme->flags & ASSOC_REQ_USE_RRM) + connect_flags |= QLINK_STA_CONNECT_USE_RRM; + + cmd->flags = cpu_to_le32(connect_flags); + memcpy(&cmd->ht_capa, &sme->ht_capa, sizeof(cmd->ht_capa)); + memcpy(&cmd->ht_capa_mask, &sme->ht_capa_mask, + sizeof(cmd->ht_capa_mask)); + memcpy(&cmd->vht_capa, &sme->vht_capa, sizeof(cmd->vht_capa)); + memcpy(&cmd->vht_capa_mask, &sme->vht_capa_mask, + sizeof(cmd->vht_capa_mask)); + cmd->pbss = sme->pbss; + + aen = &cmd->aen; + aen->auth_type = sme->auth_type; + aen->privacy = !!sme->privacy; + cmd->mfp = sme->mfp; + aen->wpa_versions = cpu_to_le32(sme->crypto.wpa_versions); + aen->cipher_group = cpu_to_le32(sme->crypto.cipher_group); + aen->n_ciphers_pairwise = cpu_to_le32(sme->crypto.n_ciphers_pairwise); for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) - aen.ciphers_pairwise[i] = cpu_to_le32( - bss_cfg->crypto.ciphers_pairwise[i]); + aen->ciphers_pairwise[i] = + cpu_to_le32(sme->crypto.ciphers_pairwise[i]); - aen.n_akm_suites = cpu_to_le32(bss_cfg->crypto.n_akm_suites); + aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites); for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) - aen.akm_suites[i] = cpu_to_le32( - bss_cfg->crypto.akm_suites[i]); + aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]); - aen.control_port = bss_cfg->crypto.control_port; - aen.control_port_no_encrypt = - bss_cfg->crypto.control_port_no_encrypt; - aen.control_port_ethertype = cpu_to_le16(be16_to_cpu( - bss_cfg->crypto.control_port_ethertype)); + aen->control_port = sme->crypto.control_port; + aen->control_port_no_encrypt = + sme->crypto.control_port_no_encrypt; + aen->control_port_ethertype = + cpu_to_le16(be16_to_cpu(sme->crypto.control_port_ethertype)); - qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, bss_cfg->ssid, - bss_cfg->ssid_len); - qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_CRYPTO, (u8 *)&aen, - sizeof(aen)); + qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, sme->ssid, + sme->ssid_len); if (sme->ie_len != 0) - qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_IE_SET, - sme->ie, - sme->ie_len); + qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_REQ, + sme->ie, sme->ie_len); + + if (sme->channel) + qtnf_cmd_channel_tlv_add(cmd_skb, sme->channel); + + qtnf_bus_lock(vif->mac->bus); ret = qtnf_cmd_send(vif->mac->bus, cmd_skb, &res_code); @@ -1976,3 +2296,219 @@ out: qtnf_bus_unlock(vif->mac->bus); return ret; } + +int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req) +{ + struct sk_buff *cmd_skb; + int ret; + u16 res_code; + struct qlink_cmd_reg_notify *cmd; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, + QLINK_CMD_REG_NOTIFY, + sizeof(*cmd)); + if (!cmd_skb) + return -ENOMEM; + + cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data; + cmd->alpha2[0] = req->alpha2[0]; + cmd->alpha2[1] = req->alpha2[1]; + + switch (req->initiator) { + case NL80211_REGDOM_SET_BY_CORE: + cmd->initiator = QLINK_REGDOM_SET_BY_CORE; + break; + case NL80211_REGDOM_SET_BY_USER: + cmd->initiator = QLINK_REGDOM_SET_BY_USER; + break; + case NL80211_REGDOM_SET_BY_DRIVER: + cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER; + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE; + break; + } + + switch (req->user_reg_hint_type) { + case NL80211_USER_REG_HINT_USER: + cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER; + break; + case NL80211_USER_REG_HINT_CELL_BASE: + cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE; + break; + case NL80211_USER_REG_HINT_INDOOR: + cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR; + break; + } + + qtnf_bus_lock(bus); + + ret = qtnf_cmd_send(bus, cmd_skb, &res_code); + if (ret) + goto out; + + switch (res_code) { + case QLINK_CMD_RESULT_ENOTSUPP: + pr_warn("reg update not supported\n"); + ret = -EOPNOTSUPP; + break; + case QLINK_CMD_RESULT_EALREADY: + pr_info("regulatory domain is already set to %c%c", + req->alpha2[0], req->alpha2[1]); + ret = -EALREADY; + break; + case QLINK_CMD_RESULT_OK: + ret = 0; + break; + default: + ret = -EFAULT; + break; + } + +out: + qtnf_bus_unlock(bus); + + return ret; +} + +int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, + struct qtnf_chan_stats *stats) +{ + struct sk_buff *cmd_skb, *resp_skb = NULL; + struct qlink_cmd_get_chan_stats *cmd; + struct qlink_resp_get_chan_stats *resp; + size_t var_data_len; + u16 res_code = QLINK_CMD_RESULT_OK; + int ret = 0; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, + QLINK_CMD_CHAN_STATS, + sizeof(*cmd)); + if (!cmd_skb) + return -ENOMEM; + + qtnf_bus_lock(mac->bus); + + cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; + cmd->channel = cpu_to_le16(channel); + + ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, &res_code, + sizeof(*resp), &var_data_len); + if (unlikely(ret)) { + qtnf_bus_unlock(mac->bus); + return ret; + } + + if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { + switch (res_code) { + case QLINK_CMD_RESULT_ENOTFOUND: + ret = -ENOENT; + break; + default: + pr_err("cmd exec failed: 0x%.4X\n", res_code); + ret = -EFAULT; + break; + } + goto out; + } + + resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; + ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info, + var_data_len); + +out: + qtnf_bus_unlock(mac->bus); + consume_skb(resp_skb); + return ret; +} + +int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, + struct cfg80211_csa_settings *params) +{ + struct qtnf_wmac *mac = vif->mac; + struct qlink_cmd_chan_switch *cmd; + struct sk_buff *cmd_skb; + u16 res_code = QLINK_CMD_RESULT_OK; + int ret; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid, + QLINK_CMD_CHAN_SWITCH, + sizeof(*cmd)); + + if (unlikely(!cmd_skb)) + return -ENOMEM; + + qtnf_bus_lock(mac->bus); + + cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; + cmd->channel = cpu_to_le16(params->chandef.chan->hw_value); + cmd->radar_required = params->radar_required; + cmd->block_tx = params->block_tx; + cmd->beacon_count = params->count; + + ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code); + + if (unlikely(ret)) + goto out; + + switch (res_code) { + case QLINK_CMD_RESULT_OK: + ret = 0; + break; + case QLINK_CMD_RESULT_ENOTFOUND: + ret = -ENOENT; + break; + case QLINK_CMD_RESULT_ENOTSUPP: + ret = -EOPNOTSUPP; + break; + case QLINK_CMD_RESULT_EALREADY: + ret = -EALREADY; + break; + case QLINK_CMD_RESULT_INVALID: + default: + ret = -EFAULT; + break; + } + +out: + qtnf_bus_unlock(mac->bus); + return ret; +} + +int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef) +{ + struct qtnf_bus *bus = vif->mac->bus; + const struct qlink_resp_channel_get *resp; + struct sk_buff *cmd_skb; + struct sk_buff *resp_skb = NULL; + u16 res_code = QLINK_CMD_RESULT_OK; + int ret; + + cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, + QLINK_CMD_CHAN_GET, + sizeof(struct qlink_cmd)); + if (unlikely(!cmd_skb)) + return -ENOMEM; + + qtnf_bus_lock(bus); + + ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, &res_code, + sizeof(*resp), NULL); + + qtnf_bus_unlock(bus); + + if (unlikely(ret)) + goto out; + + if (unlikely(res_code != QLINK_CMD_RESULT_OK)) { + ret = -ENODATA; + goto out; + } + + resp = (const struct qlink_resp_channel_get *)resp_skb->data; + qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef); + +out: + consume_skb(resp_skb); + return ret; +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h index 6c51854ef5e7..d981a76e5835 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.h +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h @@ -30,11 +30,11 @@ int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype, int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif, enum nl80211_iftype iftype, u8 *mac_addr); int qtnf_cmd_send_del_intf(struct qtnf_vif *vif); -int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, - struct ieee80211_supported_band *band); +int qtnf_cmd_band_info_get(struct qtnf_wmac *mac, + struct ieee80211_supported_band *band); int qtnf_cmd_send_regulatory_config(struct qtnf_wmac *mac, const char *alpha2); -int qtnf_cmd_send_config_ap(struct qtnf_vif *vif); -int qtnf_cmd_send_start_ap(struct qtnf_vif *vif); +int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, + const struct cfg80211_ap_settings *s); int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif); int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg); int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags, @@ -70,5 +70,11 @@ int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code); int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up); +int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req); +int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, + struct qtnf_chan_stats *stats); +int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, + struct cfg80211_csa_settings *params); +int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef); #endif /* QLINK_COMMANDS_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c index f053532c0e87..3423dc51198b 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.c +++ b/drivers/net/wireless/quantenna/qtnfmac/core.c @@ -171,7 +171,7 @@ static int qtnf_mac_init_single_band(struct wiphy *wiphy, wiphy->bands[band]->band = band; - ret = qtnf_cmd_get_mac_chan_info(mac, wiphy->bands[band]); + ret = qtnf_cmd_band_info_get(mac, wiphy->bands[band]); if (ret) { pr_err("MAC%u: band %u: failed to get chans info: %d\n", mac->macid, band, ret); @@ -179,7 +179,6 @@ static int qtnf_mac_init_single_band(struct wiphy *wiphy, } qtnf_band_init_rates(wiphy->bands[band]); - qtnf_band_setup_htvht_caps(&mac->macinfo, wiphy->bands[band]); return 0; } @@ -288,6 +287,8 @@ static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus, mac->iflist[i].mac = mac; mac->iflist[i].vifid = i; qtnf_sta_list_init(&mac->iflist[i].sta_list); + mutex_init(&mac->mac_lock); + timer_setup(&mac->scan_timeout, NULL, 0); } qtnf_mac_init_primary_intf(mac); @@ -549,6 +550,9 @@ void qtnf_core_detach(struct qtnf_bus *bus) destroy_workqueue(bus->workqueue); } + kfree(bus->hw_info.rd); + bus->hw_info.rd = NULL; + qtnf_trans_free(bus); } EXPORT_SYMBOL_GPL(qtnf_core_detach); @@ -613,6 +617,33 @@ out: } EXPORT_SYMBOL_GPL(qtnf_classify_skb); +void qtnf_wake_all_queues(struct net_device *ndev) +{ + struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); + struct qtnf_wmac *mac; + struct qtnf_bus *bus; + int macid; + int i; + + if (unlikely(!vif || !vif->mac || !vif->mac->bus)) + return; + + bus = vif->mac->bus; + + for (macid = 0; macid < QTNF_MAX_MAC; macid++) { + if (!(bus->hw_info.mac_bitmap & BIT(macid))) + continue; + + mac = bus->mac[macid]; + for (i = 0; i < QTNF_MAX_INTF; i++) { + vif = &mac->iflist[i]; + if (vif->netdev && netif_queue_stopped(vif->netdev)) + netif_tx_wake_all_queues(vif->netdev); + } + } +} +EXPORT_SYMBOL_GPL(qtnf_wake_all_queues); + MODULE_AUTHOR("Quantenna Communications"); MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver."); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h index a616434281cf..1b7bc0318f3e 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/core.h +++ b/drivers/net/wireless/quantenna/qtnfmac/core.h @@ -42,38 +42,21 @@ #define QTNF_MAX_SSID_LIST_LENGTH 2 #define QTNF_MAX_VSIE_LEN 255 -#define QTNF_MAX_ALPHA_LEN 2 #define QTNF_MAX_INTF 8 #define QTNF_MAX_EVENT_QUEUE_LEN 255 #define QTNF_DEFAULT_BG_SCAN_PERIOD 300 #define QTNF_MAX_BG_SCAN_PERIOD 0xffff +#define QTNF_SCAN_TIMEOUT_SEC 15 #define QTNF_DEF_BSS_PRIORITY 0 #define QTNF_DEF_WDOG_TIMEOUT 5 #define QTNF_TX_TIMEOUT_TRSHLD 100 -#define QTNF_STATE_AP_CONFIG BIT(2) -#define QTNF_STATE_AP_START BIT(1) - extern const struct net_device_ops qtnf_netdev_ops; + struct qtnf_bus; struct qtnf_vif; -struct qtnf_bss_config { - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bssid[ETH_ALEN]; - size_t ssid_len; - u8 dtim; - u16 bcn_period; - u16 auth_type; - bool privacy; - enum nl80211_mfp mfp; - struct cfg80211_chan_def chandef; - struct cfg80211_crypto_settings crypto; - u16 bg_scan_period; - u32 connect_flags; -}; - struct qtnf_sta_node { struct list_head list; u8 mac_addr[ETH_ALEN]; @@ -92,6 +75,8 @@ enum qtnf_sta_state { struct qtnf_vif { struct wireless_dev wdev; + u8 bssid[ETH_ALEN]; + u8 mac_addr[ETH_ALEN]; u8 vifid; u8 bss_priority; u8 bss_status; @@ -99,16 +84,14 @@ struct qtnf_vif { u16 mgmt_frames_bitmask; struct net_device *netdev; struct qtnf_wmac *mac; - u8 mac_addr[ETH_ALEN]; + struct work_struct reset_work; - struct qtnf_bss_config bss_cfg; struct qtnf_sta_list sta_list; unsigned long cons_tx_timeout_cnt; }; struct qtnf_mac_info { u8 bands_cap; - u8 phymode_cap; u8 dev_mac[ETH_ALEN]; u8 num_tx_chain; u8 num_rx_chain; @@ -119,12 +102,21 @@ struct qtnf_mac_info { u8 sretry_limit; u8 coverage_class; u8 radar_detect_widths; - struct ieee80211_ht_cap ht_cap; - struct ieee80211_vht_cap vht_cap; + struct ieee80211_ht_cap ht_cap_mod_mask; + struct ieee80211_vht_cap vht_cap_mod_mask; struct ieee80211_iface_limit *limits; size_t n_limits; }; +struct qtnf_chan_stats { + u32 chan_num; + u32 cca_tx; + u32 cca_rx; + u32 cca_busy; + u32 cca_try; + s8 chan_noise; +}; + struct qtnf_wmac { u8 macid; u8 wiphy_registered; @@ -133,17 +125,19 @@ struct qtnf_wmac { struct qtnf_mac_info macinfo; struct qtnf_vif iflist[QTNF_MAX_INTF]; struct cfg80211_scan_request *scan_req; + struct mutex mac_lock; /* lock during wmac speicific ops */ + struct timer_list scan_timeout; }; struct qtnf_hw_info { + u16 ql_proto_ver; u8 num_mac; u8 mac_bitmap; - u8 alpha2_code[QTNF_MAX_ALPHA_LEN]; u32 fw_ver; - u16 ql_proto_ver; + u32 hw_capab; + struct ieee80211_regdomain *rd; u8 total_tx_chain; u8 total_rx_chain; - u32 hw_capab; }; struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac); @@ -158,9 +152,7 @@ int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac); struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid); struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb); -struct net_device *qtnf_classify_skb_no_mbss(struct qtnf_bus *bus, - struct sk_buff *skb); - +void qtnf_wake_all_queues(struct net_device *ndev); void qtnf_virtual_intf_cleanup(struct net_device *ndev); void qtnf_netdev_updown(struct net_device *ndev, bool up); diff --git a/drivers/net/wireless/quantenna/qtnfmac/event.c b/drivers/net/wireless/quantenna/qtnfmac/event.c index 9b61e9a83670..4abc6d9ed560 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/event.c +++ b/drivers/net/wireless/quantenna/qtnfmac/event.c @@ -25,6 +25,7 @@ #include "trans.h" #include "util.h" #include "event.h" +#include "qlink_util.h" static int qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif, @@ -52,12 +53,6 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif, return -EPROTO; } - if (!(vif->bss_status & QTNF_STATE_AP_START)) { - pr_err("VIF%u.%u: STA_ASSOC event when AP is not started\n", - mac->macid, vif->vifid); - return -EPROTO; - } - sta_addr = sta_assoc->sta_addr; frame_control = le16_to_cpu(sta_assoc->frame_control); @@ -70,34 +65,39 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif, sinfo.assoc_req_ies_len = 0; payload_len = len - sizeof(*sta_assoc); - tlv = (struct qlink_tlv_hdr *)sta_assoc->ies; + tlv = (const struct qlink_tlv_hdr *)sta_assoc->ies; - while (payload_len >= sizeof(struct qlink_tlv_hdr)) { + while (payload_len >= sizeof(*tlv)) { tlv_type = le16_to_cpu(tlv->type); tlv_value_len = le16_to_cpu(tlv->len); tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); - if (tlv_full_len > payload_len) { - pr_warn("VIF%u.%u: malformed TLV 0x%.2X; LEN: %u\n", - mac->macid, vif->vifid, tlv_type, - tlv_value_len); + if (tlv_full_len > payload_len) return -EINVAL; - } if (tlv_type == QTN_TLV_ID_IE_SET) { - sinfo.assoc_req_ies = tlv->val; - sinfo.assoc_req_ies_len = tlv_value_len; + const struct qlink_tlv_ie_set *ie_set; + unsigned int ie_len; + + if (payload_len < sizeof(*ie_set)) + return -EINVAL; + + ie_set = (const struct qlink_tlv_ie_set *)tlv; + ie_len = tlv_value_len - + (sizeof(*ie_set) - sizeof(ie_set->hdr)); + + if (ie_set->type == QLINK_IE_SET_ASSOC_REQ && ie_len) { + sinfo.assoc_req_ies = ie_set->ie_data; + sinfo.assoc_req_ies_len = ie_len; + } } payload_len -= tlv_full_len; tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } - if (payload_len) { - pr_warn("VIF%u.%u: malformed TLV buf; bytes left: %zu\n", - mac->macid, vif->vifid, payload_len); + if (payload_len) return -EINVAL; - } cfg80211_new_sta(vif->netdev, sta_assoc->sta_addr, &sinfo, GFP_KERNEL); @@ -126,12 +126,6 @@ qtnf_event_handle_sta_deauth(struct qtnf_wmac *mac, struct qtnf_vif *vif, return -EPROTO; } - if (!(vif->bss_status & QTNF_STATE_AP_START)) { - pr_err("VIF%u.%u: STA_DEAUTH event when AP is not started\n", - mac->macid, vif->vifid); - return -EPROTO; - } - sta_addr = sta_deauth->sta_addr; reason = le16_to_cpu(sta_deauth->reason); @@ -211,8 +205,8 @@ qtnf_event_handle_bss_leave(struct qtnf_vif *vif, pr_debug("VIF%u.%u: disconnected\n", vif->mac->macid, vif->vifid); - cfg80211_disconnected(vif->netdev, leave_info->reason, NULL, 0, 0, - GFP_KERNEL); + cfg80211_disconnected(vif->netdev, le16_to_cpu(leave_info->reason), + NULL, 0, 0, GFP_KERNEL); vif->sta_state = QTNF_STA_DISCONNECTED; netif_carrier_off(vif->netdev); @@ -258,13 +252,12 @@ qtnf_event_handle_scan_results(struct qtnf_vif *vif, struct cfg80211_bss *bss; struct ieee80211_channel *channel; struct wiphy *wiphy = priv_to_wiphy(vif->mac); - enum cfg80211_bss_frame_type frame_type; + enum cfg80211_bss_frame_type frame_type = CFG80211_BSS_FTYPE_UNKNOWN; size_t payload_len; u16 tlv_type; u16 tlv_value_len; size_t tlv_full_len; const struct qlink_tlv_hdr *tlv; - const u8 *ies = NULL; size_t ies_len = 0; @@ -281,17 +274,6 @@ qtnf_event_handle_scan_results(struct qtnf_vif *vif, return -EINVAL; } - switch (sr->frame_type) { - case QLINK_BSS_FTYPE_BEACON: - frame_type = CFG80211_BSS_FTYPE_BEACON; - break; - case QLINK_BSS_FTYPE_PRESP: - frame_type = CFG80211_BSS_FTYPE_PRESP; - break; - default: - frame_type = CFG80211_BSS_FTYPE_UNKNOWN; - } - payload_len = len - sizeof(*sr); tlv = (struct qlink_tlv_hdr *)sr->payload; @@ -300,27 +282,43 @@ qtnf_event_handle_scan_results(struct qtnf_vif *vif, tlv_value_len = le16_to_cpu(tlv->len); tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); - if (tlv_full_len > payload_len) { - pr_warn("VIF%u.%u: malformed TLV 0x%.2X; LEN: %u\n", - vif->mac->macid, vif->vifid, tlv_type, - tlv_value_len); + if (tlv_full_len > payload_len) return -EINVAL; - } if (tlv_type == QTN_TLV_ID_IE_SET) { - ies = tlv->val; - ies_len = tlv_value_len; + const struct qlink_tlv_ie_set *ie_set; + unsigned int ie_len; + + if (payload_len < sizeof(*ie_set)) + return -EINVAL; + + ie_set = (const struct qlink_tlv_ie_set *)tlv; + ie_len = tlv_value_len - + (sizeof(*ie_set) - sizeof(ie_set->hdr)); + + switch (ie_set->type) { + case QLINK_IE_SET_BEACON_IES: + frame_type = CFG80211_BSS_FTYPE_BEACON; + break; + case QLINK_IE_SET_PROBE_RESP_IES: + frame_type = CFG80211_BSS_FTYPE_PRESP; + break; + default: + frame_type = CFG80211_BSS_FTYPE_UNKNOWN; + } + + if (ie_len) { + ies = ie_set->ie_data; + ies_len = ie_len; + } } payload_len -= tlv_full_len; tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); } - if (payload_len) { - pr_warn("VIF%u.%u: malformed TLV buf; bytes left: %zu\n", - vif->mac->macid, vif->vifid, payload_len); + if (payload_len) return -EINVAL; - } bss = cfg80211_inform_bss(wiphy, channel, frame_type, sr->bssid, get_unaligned_le64(&sr->tsf), @@ -350,6 +348,52 @@ qtnf_event_handle_scan_complete(struct qtnf_wmac *mac, return 0; } +static int +qtnf_event_handle_freq_change(struct qtnf_wmac *mac, + const struct qlink_event_freq_change *data, + u16 len) +{ + struct wiphy *wiphy = priv_to_wiphy(mac); + struct cfg80211_chan_def chandef; + struct qtnf_vif *vif; + int i; + + if (len < sizeof(*data)) { + pr_err("MAC%u: payload is too short\n", mac->macid); + return -EINVAL; + } + + if (!wiphy->registered) + return 0; + + qlink_chandef_q2cfg(wiphy, &data->chan, &chandef); + + if (!cfg80211_chandef_valid(&chandef)) { + pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n", mac->macid, + chandef.center_freq1, chandef.center_freq2, + chandef.width); + return -EINVAL; + } + + pr_debug("MAC%d: new channel ieee=%u freq1=%u freq2=%u bw=%u\n", + mac->macid, chandef.chan->hw_value, chandef.center_freq1, + chandef.center_freq2, chandef.width); + + for (i = 0; i < QTNF_MAX_INTF; i++) { + vif = &mac->iflist[i]; + if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) + continue; + + if (vif->netdev) { + mutex_lock(&vif->wdev.mtx); + cfg80211_ch_switch_notify(vif->netdev, &chandef); + mutex_unlock(&vif->wdev.mtx); + } + } + + return 0; +} + static int qtnf_event_parse(struct qtnf_wmac *mac, const struct sk_buff *event_skb) { @@ -400,6 +444,10 @@ static int qtnf_event_parse(struct qtnf_wmac *mac, ret = qtnf_event_handle_bss_leave(vif, (const void *)event, event_len); break; + case QLINK_EVENT_FREQ_CHANGE: + ret = qtnf_event_handle_freq_change(mac, (const void *)event, + event_len); + break; default: pr_warn("unknown event type: %x\n", event_id); break; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c index 7fc4f0d6a9ad..7e487622d87d 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c @@ -25,6 +25,8 @@ #include <linux/completion.h> #include <linux/crc32.h> #include <linux/spinlock.h> +#include <linux/circ_buf.h> +#include <linux/log2.h> #include "qtn_hw_ids.h" #include "pcie_bus_priv.h" @@ -36,17 +38,13 @@ static bool use_msi = true; module_param(use_msi, bool, 0644); MODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt"); -static unsigned int tx_bd_size_param = 256; +static unsigned int tx_bd_size_param = 32; module_param(tx_bd_size_param, uint, 0644); -MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size"); +MODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size, power of two"); static unsigned int rx_bd_size_param = 256; module_param(rx_bd_size_param, uint, 0644); -MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size"); - -static unsigned int rx_bd_reserved_param = 16; -module_param(rx_bd_reserved_param, uint, 0644); -MODULE_PARM_DESC(rx_bd_reserved_param, "Reserved RX descriptors"); +MODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size, power of two"); static u8 flashboot = 1; module_param(flashboot, byte, 0644); @@ -186,8 +184,10 @@ static void __iomem *qtnf_map_bar(struct qtnf_pcie_bus_priv *priv, u8 index) return IOMEM_ERR_PTR(ret); busaddr = pci_resource_start(priv->pdev, index); - vaddr = pcim_iomap_table(priv->pdev)[index]; len = pci_resource_len(priv->pdev, index); + vaddr = pcim_iomap_table(priv->pdev)[index]; + if (!vaddr) + return IOMEM_ERR_PTR(-ENOMEM); pr_debug("BAR%u vaddr=0x%p busaddr=%pad len=%u\n", index, vaddr, &busaddr, (int)len); @@ -250,19 +250,19 @@ static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv) int ret = -ENOMEM; priv->sysctl_bar = qtnf_map_bar(priv, QTN_SYSCTL_BAR); - if (IS_ERR_OR_NULL(priv->sysctl_bar)) { + if (IS_ERR(priv->sysctl_bar)) { pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR); return ret; } priv->dmareg_bar = qtnf_map_bar(priv, QTN_DMA_BAR); - if (IS_ERR_OR_NULL(priv->dmareg_bar)) { + if (IS_ERR(priv->dmareg_bar)) { pr_err("failed to map BAR%u\n", QTN_DMA_BAR); return ret; } priv->epmem_bar = qtnf_map_bar(priv, QTN_SHMEM_BAR); - if (IS_ERR_OR_NULL(priv->epmem_bar)) { + if (IS_ERR(priv->epmem_bar)) { pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR); return ret; } @@ -274,32 +274,6 @@ static int qtnf_pcie_init_memory(struct qtnf_pcie_bus_priv *priv) return 0; } -static int -qtnf_pcie_init_dma_mask(struct qtnf_pcie_bus_priv *priv, u64 dma_mask) -{ - int ret; - - ret = dma_supported(&priv->pdev->dev, dma_mask); - if (!ret) { - pr_err("DMA mask %llu not supported\n", dma_mask); - return ret; - } - - ret = pci_set_dma_mask(priv->pdev, dma_mask); - if (ret) { - pr_err("failed to set DMA mask %llu\n", dma_mask); - return ret; - } - - ret = pci_set_consistent_dma_mask(priv->pdev, dma_mask); - if (ret) { - pr_err("failed to set consistent DMA mask %llu\n", dma_mask); - return ret; - } - - return ret; -} - static void qtnf_tune_pcie_mps(struct qtnf_pcie_bus_priv *priv) { struct pci_dev *pdev = priv->pdev; @@ -418,9 +392,8 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); - priv->tx_bd_reclaim_start = 0; - priv->tx_bd_index = 0; - priv->tx_queue_len = 0; + priv->tx_bd_r_index = 0; + priv->tx_bd_w_index = 0; /* rx bd */ @@ -430,43 +403,34 @@ static int alloc_bd_table(struct qtnf_pcie_bus_priv *priv) priv->rx_bd_vbase = vaddr; priv->rx_bd_pbase = paddr; - writel(QTN_HOST_LO32(paddr), - PCIE_HDP_TX_HOST_Q_BASE_L(priv->pcie_reg_base)); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT writel(QTN_HOST_HI32(paddr), PCIE_HDP_TX_HOST_Q_BASE_H(priv->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(paddr), + PCIE_HDP_TX_HOST_Q_BASE_L(priv->pcie_reg_base)); writel(priv->rx_bd_num | (sizeof(struct qtnf_rx_bd)) << 16, PCIE_HDP_TX_HOST_Q_SZ_CTRL(priv->pcie_reg_base)); - priv->hw_txproc_wr_ptr = priv->rx_bd_num - rx_bd_reserved_param; - - writel(priv->hw_txproc_wr_ptr, - PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); - pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr); - priv->rx_bd_index = 0; - return 0; } -static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) +static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 index) { struct qtnf_rx_bd *rxbd; struct sk_buff *skb; dma_addr_t paddr; - skb = __dev_alloc_skb(SKB_BUF_SIZE + NET_IP_ALIGN, - GFP_ATOMIC); + skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC); if (!skb) { - priv->rx_skb[rx_bd_index] = NULL; + priv->rx_skb[index] = NULL; return -ENOMEM; } - priv->rx_skb[rx_bd_index] = skb; - - skb_reserve(skb, NET_IP_ALIGN); - - rxbd = &priv->rx_bd_vbase[rx_bd_index]; + priv->rx_skb[index] = skb; + rxbd = &priv->rx_bd_vbase[index]; paddr = pci_map_single(priv->pdev, skb->data, SKB_BUF_SIZE, PCI_DMA_FROMDEVICE); @@ -475,17 +439,24 @@ static int skb2rbd_attach(struct qtnf_pcie_bus_priv *priv, u16 rx_bd_index) return -ENOMEM; } - writel(QTN_HOST_LO32(paddr), - PCIE_HDP_HHBM_BUF_PTR(priv->pcie_reg_base)); - writel(QTN_HOST_HI32(paddr), - PCIE_HDP_HHBM_BUF_PTR_H(priv->pcie_reg_base)); - /* keep rx skb paddrs in rx buffer descriptors for cleanup purposes */ rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr)); rxbd->addr_h = cpu_to_le32(QTN_HOST_HI32(paddr)); - rxbd->info = 0x0; + priv->rx_bd_w_index = index; + + /* sync up all descriptor updates */ + wmb(); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + writel(QTN_HOST_HI32(paddr), + PCIE_HDP_HHBM_BUF_PTR_H(priv->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(paddr), + PCIE_HDP_HHBM_BUF_PTR(priv->pcie_reg_base)); + + writel(index, PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); return 0; } @@ -516,7 +487,7 @@ static void free_xfer_buffers(void *data) /* free rx buffers */ for (i = 0; i < priv->rx_bd_num; i++) { - if (priv->rx_skb[i]) { + if (priv->rx_skb && priv->rx_skb[i]) { rxbd = &priv->rx_bd_vbase[i]; paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), le32_to_cpu(rxbd->addr)); @@ -529,19 +500,72 @@ static void free_xfer_buffers(void *data) /* free tx buffers */ for (i = 0; i < priv->tx_bd_num; i++) { - if (priv->tx_skb[i]) { + if (priv->tx_skb && priv->tx_skb[i]) { dev_kfree_skb_any(priv->tx_skb[i]); priv->tx_skb[i] = NULL; } } } +static int qtnf_hhbm_init(struct qtnf_pcie_bus_priv *priv) +{ + u32 val; + + val = readl(PCIE_HHBM_CONFIG(priv->pcie_reg_base)); + val |= HHBM_CONFIG_SOFT_RESET; + writel(val, PCIE_HHBM_CONFIG(priv->pcie_reg_base)); + usleep_range(50, 100); + val &= ~HHBM_CONFIG_SOFT_RESET; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + val |= HHBM_64BIT; +#endif + writel(val, PCIE_HHBM_CONFIG(priv->pcie_reg_base)); + writel(priv->rx_bd_num, PCIE_HHBM_Q_LIMIT_REG(priv->pcie_reg_base)); + + return 0; +} + static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) { int ret; + u32 val; priv->tx_bd_num = tx_bd_size_param; priv->rx_bd_num = rx_bd_size_param; + priv->rx_bd_w_index = 0; + priv->rx_bd_r_index = 0; + + if (!priv->tx_bd_num || !is_power_of_2(priv->tx_bd_num)) { + pr_err("tx_bd_size_param %u is not power of two\n", + priv->tx_bd_num); + return -EINVAL; + } + + val = priv->tx_bd_num * sizeof(struct qtnf_tx_bd); + if (val > PCIE_HHBM_MAX_SIZE) { + pr_err("tx_bd_size_param %u is too large\n", + priv->tx_bd_num); + return -EINVAL; + } + + if (!priv->rx_bd_num || !is_power_of_2(priv->rx_bd_num)) { + pr_err("rx_bd_size_param %u is not power of two\n", + priv->rx_bd_num); + return -EINVAL; + } + + val = priv->rx_bd_num * sizeof(dma_addr_t); + if (val > PCIE_HHBM_MAX_SIZE) { + pr_err("rx_bd_size_param %u is too large\n", + priv->rx_bd_num); + return -EINVAL; + } + + ret = qtnf_hhbm_init(priv); + if (ret) { + pr_err("failed to init h/w queues\n"); + return ret; + } ret = alloc_skb_array(priv); if (ret) { @@ -564,67 +588,73 @@ static int qtnf_pcie_init_xfer(struct qtnf_pcie_bus_priv *priv) return ret; } -static int qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) +static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv) { struct qtnf_tx_bd *txbd; struct sk_buff *skb; + unsigned long flags; dma_addr_t paddr; - int last_sent; - int count; + u32 tx_done_index; + int count = 0; int i; - last_sent = readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) - % priv->tx_bd_num; - i = priv->tx_bd_reclaim_start; - count = 0; + spin_lock_irqsave(&priv->tx_reclaim_lock, flags); - while (i != last_sent) { - skb = priv->tx_skb[i]; - if (!skb) - break; + tx_done_index = readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) + & (priv->tx_bd_num - 1); - txbd = &priv->tx_bd_vbase[i]; - paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), - le32_to_cpu(txbd->addr)); - pci_unmap_single(priv->pdev, paddr, skb->len, PCI_DMA_TODEVICE); + i = priv->tx_bd_r_index; - if (skb->dev) { - skb->dev->stats.tx_packets++; - skb->dev->stats.tx_bytes += skb->len; + while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) { + skb = priv->tx_skb[i]; + if (likely(skb)) { + txbd = &priv->tx_bd_vbase[i]; + paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h), + le32_to_cpu(txbd->addr)); + pci_unmap_single(priv->pdev, paddr, skb->len, + PCI_DMA_TODEVICE); + + if (skb->dev) { + skb->dev->stats.tx_packets++; + skb->dev->stats.tx_bytes += skb->len; + if (unlikely(priv->tx_stopped)) { + qtnf_wake_all_queues(skb->dev); + priv->tx_stopped = 0; + } + } - if (netif_queue_stopped(skb->dev)) - netif_wake_queue(skb->dev); + dev_kfree_skb_any(skb); } - dev_kfree_skb_any(skb); priv->tx_skb[i] = NULL; - priv->tx_queue_len--; count++; if (++i >= priv->tx_bd_num) i = 0; } - priv->tx_bd_reclaim_start = i; priv->tx_reclaim_done += count; priv->tx_reclaim_req++; + priv->tx_bd_r_index = i; - return count; + spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags); } -static bool qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv) +static int qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv) { - if (priv->tx_queue_len >= priv->tx_bd_num - 1) { - pr_err_ratelimited("reclaim full Tx queue\n"); + if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)) { qtnf_pcie_data_tx_reclaim(priv); - if (priv->tx_queue_len >= priv->tx_bd_num - 1) { + if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)) { + pr_warn_ratelimited("reclaim full Tx queue\n"); priv->tx_full_count++; - return false; + return 0; } } - return true; + return 1; } static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) @@ -637,19 +667,19 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) u32 info; int ret = 0; - spin_lock_irqsave(&priv->tx_lock, flags); - - priv->tx_done_count++; + spin_lock_irqsave(&priv->tx0_lock, flags); if (!qtnf_tx_queue_ready(priv)) { - if (skb->dev) - netif_stop_queue(skb->dev); + if (skb->dev) { + netif_tx_stop_all_queues(skb->dev); + priv->tx_stopped = 1; + } - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irqrestore(&priv->tx0_lock, flags); return NETDEV_TX_BUSY; } - i = priv->tx_bd_index; + i = priv->tx_bd_w_index; priv->tx_skb[i] = skb; len = skb->len; @@ -673,16 +703,18 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) /* write new TX descriptor to PCIE_RX_FIFO on EP */ txbd_paddr = priv->tx_bd_pbase + i * sizeof(struct qtnf_tx_bd); - writel(QTN_HOST_LO32(txbd_paddr), - PCIE_HDP_HOST_WR_DESC0(priv->pcie_reg_base)); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT writel(QTN_HOST_HI32(txbd_paddr), PCIE_HDP_HOST_WR_DESC0_H(priv->pcie_reg_base)); +#endif + writel(QTN_HOST_LO32(txbd_paddr), + PCIE_HDP_HOST_WR_DESC0(priv->pcie_reg_base)); if (++i >= priv->tx_bd_num) i = 0; - priv->tx_bd_index = i; - priv->tx_queue_len++; + priv->tx_bd_w_index = i; tx_done: if (ret && skb) { @@ -692,7 +724,10 @@ tx_done: dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&priv->tx_lock, flags); + priv->tx_done_count++; + spin_unlock_irqrestore(&priv->tx0_lock, flags); + + qtnf_pcie_data_tx_reclaim(priv); return NETDEV_TX_OK; } @@ -719,14 +754,21 @@ static irqreturn_t qtnf_interrupt(int irq, void *data) if (!(status & priv->pcie_irq_mask)) goto irq_done; - if (status & PCIE_HDP_INT_RX_BITS) { + if (status & PCIE_HDP_INT_RX_BITS) priv->pcie_irq_rx_count++; + + if (status & PCIE_HDP_INT_TX_BITS) + priv->pcie_irq_tx_count++; + + if (status & PCIE_HDP_INT_HHBM_UF) + priv->pcie_irq_uf_count++; + + if (status & PCIE_HDP_INT_RX_BITS) { qtnf_dis_rxdone_irq(priv); napi_schedule(&bus->mux_napi); } if (status & PCIE_HDP_INT_TX_BITS) { - priv->pcie_irq_tx_count++; qtnf_dis_txdone_irq(priv); tasklet_hi_schedule(&priv->reclaim_tq); } @@ -741,16 +783,19 @@ irq_done: return IRQ_HANDLED; } -static inline void hw_txproc_wr_ptr_inc(struct qtnf_pcie_bus_priv *priv) +static int qtnf_rx_data_ready(struct qtnf_pcie_bus_priv *priv) { - u32 index; + u16 index = priv->rx_bd_r_index; + struct qtnf_rx_bd *rxbd; + u32 descw; - index = priv->hw_txproc_wr_ptr; + rxbd = &priv->rx_bd_vbase[index]; + descw = le32_to_cpu(rxbd->info); - if (++index >= priv->rx_bd_num) - index = 0; + if (descw & QTN_TXDONE_MASK) + return 1; - priv->hw_txproc_wr_ptr = index; + return 0; } static int qtnf_rx_poll(struct napi_struct *napi, int budget) @@ -762,64 +807,96 @@ static int qtnf_rx_poll(struct napi_struct *napi, int budget) int processed = 0; struct qtnf_rx_bd *rxbd; dma_addr_t skb_paddr; + int consume; u32 descw; - u16 index; + u32 psize; + u16 r_idx; + u16 w_idx; int ret; - index = priv->rx_bd_index; - rxbd = &priv->rx_bd_vbase[index]; + while (processed < budget) { - descw = le32_to_cpu(rxbd->info); - while ((descw & QTN_TXDONE_MASK) && (processed < budget)) { - skb = priv->rx_skb[index]; + if (!qtnf_rx_data_ready(priv)) + goto rx_out; - if (likely(skb)) { - skb_put(skb, QTN_GET_LEN(descw)); + r_idx = priv->rx_bd_r_index; + rxbd = &priv->rx_bd_vbase[r_idx]; + descw = le32_to_cpu(rxbd->info); + + skb = priv->rx_skb[r_idx]; + psize = QTN_GET_LEN(descw); + consume = 1; + + if (!(descw & QTN_TXDONE_MASK)) { + pr_warn("skip invalid rxbd[%d]\n", r_idx); + consume = 0; + } + + if (!skb) { + pr_warn("skip missing rx_skb[%d]\n", r_idx); + consume = 0; + } + + if (skb && (skb_tailroom(skb) < psize)) { + pr_err("skip packet with invalid length: %u > %u\n", + psize, skb_tailroom(skb)); + consume = 0; + } + if (skb) { skb_paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h), le32_to_cpu(rxbd->addr)); pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE, PCI_DMA_FROMDEVICE); + } + if (consume) { + skb_put(skb, psize); ndev = qtnf_classify_skb(bus, skb); if (likely(ndev)) { ndev->stats.rx_packets++; ndev->stats.rx_bytes += skb->len; skb->protocol = eth_type_trans(skb, ndev); - netif_receive_skb(skb); + napi_gro_receive(napi, skb); } else { pr_debug("drop untagged skb\n"); bus->mux_dev.stats.rx_dropped++; dev_kfree_skb_any(skb); } - - processed++; } else { - pr_err("missing rx_skb[%d]\n", index); + if (skb) { + bus->mux_dev.stats.rx_dropped++; + dev_kfree_skb_any(skb); + } } - /* attached rx buffer is passed upstream: map a new one */ - ret = skb2rbd_attach(priv, index); - if (likely(!ret)) { - if (++index >= priv->rx_bd_num) - index = 0; + priv->rx_skb[r_idx] = NULL; + if (++r_idx >= priv->rx_bd_num) + r_idx = 0; - priv->rx_bd_index = index; - hw_txproc_wr_ptr_inc(priv); + priv->rx_bd_r_index = r_idx; - rxbd = &priv->rx_bd_vbase[index]; - descw = le32_to_cpu(rxbd->info); - } else { - pr_err("failed to allocate new rx_skb[%d]\n", index); - break; + /* repalce processed buffer by a new one */ + w_idx = priv->rx_bd_w_index; + while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, + priv->rx_bd_num) > 0) { + if (++w_idx >= priv->rx_bd_num) + w_idx = 0; + + ret = skb2rbd_attach(priv, w_idx); + if (ret) { + pr_err("failed to allocate new rx_skb[%d]\n", + w_idx); + break; + } } - writel(priv->hw_txproc_wr_ptr, - PCIE_HDP_TX_HOST_Q_WR_PTR(priv->pcie_reg_base)); + processed++; } +rx_out: if (processed < budget) { napi_complete(napi); qtnf_en_rxdone_irq(priv); @@ -1058,11 +1135,8 @@ static int qtnf_bringup_fw(struct qtnf_bus *bus) static void qtnf_reclaim_tasklet_fn(unsigned long data) { struct qtnf_pcie_bus_priv *priv = (void *)data; - unsigned long flags; - spin_lock_irqsave(&priv->tx_lock, flags); qtnf_pcie_data_tx_reclaim(priv); - spin_unlock_irqrestore(&priv->tx_lock, flags); qtnf_en_txdone_irq(priv); } @@ -1090,10 +1164,22 @@ static int qtnf_dbg_irq_stats(struct seq_file *s, void *data) { struct qtnf_bus *bus = dev_get_drvdata(s->private); struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); + u32 reg = readl(PCIE_HDP_INT_EN(priv->pcie_reg_base)); + u32 status; seq_printf(s, "pcie_irq_count(%u)\n", priv->pcie_irq_count); seq_printf(s, "pcie_irq_tx_count(%u)\n", priv->pcie_irq_tx_count); + status = reg & PCIE_HDP_INT_TX_BITS; + seq_printf(s, "pcie_irq_tx_status(%s)\n", + (status == PCIE_HDP_INT_TX_BITS) ? "EN" : "DIS"); seq_printf(s, "pcie_irq_rx_count(%u)\n", priv->pcie_irq_rx_count); + status = reg & PCIE_HDP_INT_RX_BITS; + seq_printf(s, "pcie_irq_rx_status(%s)\n", + (status == PCIE_HDP_INT_RX_BITS) ? "EN" : "DIS"); + seq_printf(s, "pcie_irq_uf_count(%u)\n", priv->pcie_irq_uf_count); + status = reg & PCIE_HDP_INT_HHBM_UF; + seq_printf(s, "pcie_irq_hhbm_uf_status(%s)\n", + (status == PCIE_HDP_INT_HHBM_UF) ? "EN" : "DIS"); return 0; } @@ -1107,10 +1193,24 @@ static int qtnf_dbg_hdp_stats(struct seq_file *s, void *data) seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count); seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done); seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req); - seq_printf(s, "tx_bd_reclaim_start(%u)\n", priv->tx_bd_reclaim_start); - seq_printf(s, "tx_bd_index(%u)\n", priv->tx_bd_index); - seq_printf(s, "rx_bd_index(%u)\n", priv->rx_bd_index); - seq_printf(s, "tx_queue_len(%u)\n", priv->tx_queue_len); + + seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index); + seq_printf(s, "tx_bd_p_index(%u)\n", + readl(PCIE_HDP_RX0DMA_CNT(priv->pcie_reg_base)) + & (priv->tx_bd_num - 1)); + seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index); + seq_printf(s, "tx queue len(%u)\n", + CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index, + priv->tx_bd_num)); + + seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index); + seq_printf(s, "rx_bd_p_index(%u)\n", + readl(PCIE_HDP_TX0DMA_CNT(priv->pcie_reg_base)) + & (priv->rx_bd_num - 1)); + seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index); + seq_printf(s, "rx alloc queue len(%u)\n", + CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index, + priv->rx_bd_num)); return 0; } @@ -1156,8 +1256,9 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) strcpy(bus->fwname, QTN_PCI_PEARL_FW_NAME); init_completion(&bus->request_firmware_complete); mutex_init(&bus->bus_lock); + spin_lock_init(&pcie_priv->tx0_lock); spin_lock_init(&pcie_priv->irq_lock); - spin_lock_init(&pcie_priv->tx_lock); + spin_lock_init(&pcie_priv->tx_reclaim_lock); /* init stats */ pcie_priv->tx_full_count = 0; @@ -1165,6 +1266,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pcie_priv->pcie_irq_count = 0; pcie_priv->pcie_irq_rx_count = 0; pcie_priv->pcie_irq_tx_count = 0; + pcie_priv->pcie_irq_uf_count = 0; pcie_priv->tx_reclaim_done = 0; pcie_priv->tx_reclaim_req = 0; @@ -1191,6 +1293,16 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pr_debug("successful init of PCI device %x\n", pdev->device); } +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); +#else + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); +#endif + if (ret) { + pr_err("PCIE DMA coherent mask init failed\n"); + goto err_base; + } + pcim_pin_device(pdev); pci_set_master(pdev); @@ -1212,12 +1324,6 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_base; } - ret = qtnf_pcie_init_dma_mask(pcie_priv, DMA_BIT_MASK(32)); - if (ret) { - pr_err("PCIE DMA mask init failed\n"); - goto err_base; - } - ret = devm_add_action(&pdev->dev, free_xfer_buffers, (void *)pcie_priv); if (ret) { pr_err("custom release callback init failed\n"); @@ -1336,7 +1442,7 @@ static SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend, qtnf_pcie_resume); #endif -static struct pci_device_id qtnf_pcie_devid_table[] = { +static const struct pci_device_id qtnf_pcie_devid_table[] = { { PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QTN_PEARL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h index 2a897db2bd79..397875a50fc2 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h @@ -32,9 +32,12 @@ struct qtnf_pcie_bus_priv { /* lock for irq configuration changes */ spinlock_t irq_lock; - /* lock for tx operations */ - spinlock_t tx_lock; + /* lock for tx reclaim operations */ + spinlock_t tx_reclaim_lock; + /* lock for tx0 operations */ + spinlock_t tx0_lock; u8 msi_enabled; + u8 tx_stopped; int mps; struct workqueue_struct *workqueue; @@ -66,13 +69,11 @@ struct qtnf_pcie_bus_priv { void *bd_table_vaddr; u32 bd_table_len; - u32 hw_txproc_wr_ptr; + u32 rx_bd_w_index; + u32 rx_bd_r_index; - u16 tx_bd_reclaim_start; - u16 tx_bd_index; - u32 tx_queue_len; - - u16 rx_bd_index; + u32 tx_bd_w_index; + u32 tx_bd_r_index; u32 pcie_irq_mask; @@ -80,6 +81,7 @@ struct qtnf_pcie_bus_priv { u32 pcie_irq_count; u32 pcie_irq_rx_count; u32 pcie_irq_tx_count; + u32 pcie_irq_uf_count; u32 tx_full_count; u32 tx_done_count; u32 tx_reclaim_done; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h index e00d508fbcf0..c5a4e46d26ef 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_ipc.h @@ -50,22 +50,21 @@ #define PCIE_HDP_INT_RX_BITS (0 \ | PCIE_HDP_INT_EP_TXDMA \ | PCIE_HDP_INT_EP_TXEMPTY \ + | PCIE_HDP_INT_HHBM_UF \ ) #define PCIE_HDP_INT_TX_BITS (0 \ | PCIE_HDP_INT_EP_RXDMA \ ) -#if BITS_PER_LONG == 64 +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT #define QTN_HOST_HI32(a) ((u32)(((u64)a) >> 32)) #define QTN_HOST_LO32(a) ((u32)(((u64)a) & 0xffffffffUL)) #define QTN_HOST_ADDR(h, l) ((((u64)h) << 32) | ((u64)l)) -#elif BITS_PER_LONG == 32 +#else #define QTN_HOST_HI32(a) 0 #define QTN_HOST_LO32(a) ((u32)(((u32)a) & 0xffffffffUL)) #define QTN_HOST_ADDR(h, l) ((u32)l) -#else -#error Unexpected BITS_PER_LONG value #endif #define QTN_SYSCTL_BAR 0 @@ -75,7 +74,7 @@ #define QTN_PCIE_BDA_VERSION 0x1002 #define PCIE_BDA_NAMELEN 32 -#define PCIE_HHBM_MAX_SIZE 512 +#define PCIE_HHBM_MAX_SIZE 2048 #define SKB_BUF_SIZE 2048 @@ -112,7 +111,7 @@ struct qtnf_pcie_bda { __le32 bda_flashsz; u8 bda_boardname[PCIE_BDA_NAMELEN]; __le32 bda_rc_msi_enabled; - __le32 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; + u8 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; __le32 bda_dsbw_start_index; __le32 bda_dsbw_end_index; __le32 bda_dsbw_total_bytes; diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h index 78715b8a8ef9..5b48b425fa7f 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h +++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_regs_pearl.h @@ -109,6 +109,7 @@ #define HHBM_WR_REQ (BIT(0)) #define HHBM_RD_REQ (BIT(1)) #define HHBM_DONE (BIT(31)) +#define HHBM_64BIT (BIT(10)) /* offsets for dual PCIE */ #define PCIE_PORT_LINK_CTL(base) ((base) + 0x0710) @@ -333,6 +334,7 @@ #define PCIE_HDP_INT_RX_LEN_ERR (BIT(2)) #define PCIE_HDP_INT_RX_HDR_LEN_ERR (BIT(3)) #define PCIE_HDP_INT_EP_TXDMA (BIT(12)) +#define PCIE_HDP_INT_HHBM_UF (BIT(13)) #define PCIE_HDP_INT_EP_TXEMPTY (BIT(15)) #define PCIE_HDP_INT_IPC (BIT(29)) diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index 6eafc15e0065..a432fb001c41 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -19,7 +19,7 @@ #include <linux/ieee80211.h> -#define QLINK_PROTO_VER 3 +#define QLINK_PROTO_VER 6 #define QLINK_MACID_RSVD 0xFF #define QLINK_VIFID_RSVD 0xFF @@ -61,14 +61,17 @@ struct qlink_msg_header { /* Generic definitions of data and information carried in QLINK messages */ +/** + * enum qlink_hw_capab - device capabilities. + * + * @QLINK_HW_CAPAB_REG_UPDATE: device can update it's regulatory region. + * @QLINK_HW_CAPAB_STA_INACT_TIMEOUT: device implements a logic to kick-out + * associated STAs due to inactivity. Inactivity timeout period is taken + * from QLINK_CMD_START_AP parameters. + */ enum qlink_hw_capab { - QLINK_HW_SUPPORTS_REG_UPDATE = BIT(0), -}; - -enum qlink_phy_mode { - QLINK_PHYMODE_BGN = BIT(0), - QLINK_PHYMODE_AN = BIT(1), - QLINK_PHYMODE_AC = BIT(2), + QLINK_HW_CAPAB_REG_UPDATE = BIT(0), + QLINK_HW_CAPAB_STA_INACT_TIMEOUT = BIT(1), }; enum qlink_iface_type { @@ -77,6 +80,7 @@ enum qlink_iface_type { QLINK_IFTYPE_ADHOC = 3, QLINK_IFTYPE_MONITOR = 4, QLINK_IFTYPE_WDS = 5, + QLINK_IFTYPE_AP_VLAN = 6, }; /** @@ -85,12 +89,12 @@ enum qlink_iface_type { * Data describing a single virtual interface. * * @if_type: Mode of interface operation, one of &enum qlink_iface_type - * @flags: interface flagsmap. + * @vlanid: VLAN ID for AP_VLAN interface type * @mac_addr: MAC address of virtual interface. */ struct qlink_intf_info { __le16 if_type; - __le16 flags; + __le16 vlanid; u8 mac_addr[ETH_ALEN]; u8 rsvd[2]; } __packed; @@ -107,16 +111,48 @@ enum qlink_sta_flags { }; enum qlink_channel_width { - QLINK_CHAN_WIDTH_5 = BIT(0), - QLINK_CHAN_WIDTH_10 = BIT(1), - QLINK_CHAN_WIDTH_20_NOHT = BIT(2), - QLINK_CHAN_WIDTH_20 = BIT(3), - QLINK_CHAN_WIDTH_40 = BIT(4), - QLINK_CHAN_WIDTH_80 = BIT(5), - QLINK_CHAN_WIDTH_80P80 = BIT(6), - QLINK_CHAN_WIDTH_160 = BIT(7), + QLINK_CHAN_WIDTH_5 = 0, + QLINK_CHAN_WIDTH_10, + QLINK_CHAN_WIDTH_20_NOHT, + QLINK_CHAN_WIDTH_20, + QLINK_CHAN_WIDTH_40, + QLINK_CHAN_WIDTH_80, + QLINK_CHAN_WIDTH_80P80, + QLINK_CHAN_WIDTH_160, }; +/** + * struct qlink_chandef - qlink channel definition + * + * @center_freq1: center frequency of first segment + * @center_freq2: center frequency of second segment (80+80 only) + * @width: channel width, one of @enum qlink_channel_width + */ +struct qlink_chandef { + __le16 center_freq1; + __le16 center_freq2; + u8 width; + u8 rsvd[3]; +} __packed; + +#define QLINK_MAX_NR_CIPHER_SUITES 5 +#define QLINK_MAX_NR_AKM_SUITES 2 + +struct qlink_auth_encr { + __le32 wpa_versions; + __le32 cipher_group; + __le32 n_ciphers_pairwise; + __le32 ciphers_pairwise[QLINK_MAX_NR_CIPHER_SUITES]; + __le32 n_akm_suites; + __le32 akm_suites[QLINK_MAX_NR_AKM_SUITES]; + __le16 control_port_ethertype; + u8 auth_type; + u8 privacy; + u8 control_port; + u8 control_port_no_encrypt; + u8 rsvd[2]; +} __packed; + /* QLINK Command messages related definitions */ @@ -126,13 +162,17 @@ enum qlink_channel_width { * Commands are QLINK messages of type @QLINK_MSG_TYPE_CMD, sent by driver to * wireless network device for processing. Device is expected to send back a * reply message of type &QLINK_MSG_TYPE_CMDRSP, containing at least command - * execution status (one of &enum qlink_cmd_result) at least. Reply message + * execution status (one of &enum qlink_cmd_result). Reply message * may also contain data payload specific to the command type. * - * @QLINK_CMD_CHANS_INFO_GET: for the specified MAC and specified band, get - * number of operational channels and information on each of the channel. + * @QLINK_CMD_BAND_INFO_GET: for the specified MAC and specified band, get + * the band's description including number of operational channels and + * info on each channel, HT/VHT capabilities, supported rates etc. * This command is generic to a specified MAC, interface index must be set * to QLINK_VIFID_RSVD in command header. + * @QLINK_CMD_REG_NOTIFY: notify device about regulatory domain change. This + * command is supported only if device reports QLINK_HW_SUPPORTS_REG_UPDATE + * capability. */ enum qlink_cmd_type { QLINK_CMD_FW_INIT = 0x0001, @@ -148,9 +188,10 @@ enum qlink_cmd_type { QLINK_CMD_DEL_INTF = 0x0016, QLINK_CMD_CHANGE_INTF = 0x0017, QLINK_CMD_UPDOWN_INTF = 0x0018, - QLINK_CMD_REG_REGION = 0x0019, - QLINK_CMD_CHANS_INFO_GET = 0x001A, - QLINK_CMD_CONFIG_AP = 0x0020, + QLINK_CMD_REG_NOTIFY = 0x0019, + QLINK_CMD_BAND_INFO_GET = 0x001A, + QLINK_CMD_CHAN_SWITCH = 0x001B, + QLINK_CMD_CHAN_GET = 0x001C, QLINK_CMD_START_AP = 0x0021, QLINK_CMD_STOP_AP = 0x0022, QLINK_CMD_GET_STA_INFO = 0x0030, @@ -161,6 +202,7 @@ enum qlink_cmd_type { QLINK_CMD_CHANGE_STA = 0x0051, QLINK_CMD_DEL_STA = 0x0052, QLINK_CMD_SCAN = 0x0053, + QLINK_CMD_CHAN_STATS = 0x0054, QLINK_CMD_CONNECT = 0x0060, QLINK_CMD_DISCONNECT = 0x0061, }; @@ -256,21 +298,6 @@ struct qlink_cmd_mgmt_frame_tx { } __packed; /** - * struct qlink_cmd_mgmt_append_ie - data for QLINK_CMD_MGMT_SET_APPIE command - * - * @type: type of MGMT frame to appent requested IEs to, one of - * &enum qlink_mgmt_frame_type. - * @flags: for future use. - * @ie_data: IEs data to append. - */ -struct qlink_cmd_mgmt_append_ie { - struct qlink_cmd chdr; - u8 type; - u8 flags; - u8 ie_data[0]; -} __packed; - -/** * struct qlink_cmd_get_sta_info - data for QLINK_CMD_GET_STA_INFO command * * @sta_addr: MAC address of the STA statistics is requested for. @@ -287,6 +314,7 @@ struct qlink_cmd_get_sta_info { * @pairwise: whether to use pairwise key. * @addr: MAC address of a STA key is being installed to. * @cipher: cipher suite. + * @vlanid: VLAN ID for AP_VLAN interface type * @key_data: key data itself. */ struct qlink_cmd_add_key { @@ -295,6 +323,7 @@ struct qlink_cmd_add_key { u8 pairwise; u8 addr[ETH_ALEN]; __le32 cipher; + __le16 vlanid; u8 key_data[0]; } __packed; @@ -341,12 +370,16 @@ struct qlink_cmd_set_def_mgmt_key { * * @sta_flags_mask: STA flags mask, bitmap of &enum qlink_sta_flags * @sta_flags_set: STA flags values, bitmap of &enum qlink_sta_flags + * @if_type: Mode of interface operation, one of &enum qlink_iface_type + * @vlanid: VLAN ID to assign to specific STA * @sta_addr: address of the STA for which parameters are set. */ struct qlink_cmd_change_sta { struct qlink_cmd chdr; __le32 sta_flags_mask; __le32 sta_flags_set; + __le16 if_type; + __le16 vlanid; u8 sta_addr[ETH_ALEN]; } __packed; @@ -371,18 +404,36 @@ enum qlink_sta_connect_flags { /** * struct qlink_cmd_connect - data for QLINK_CMD_CONNECT command * - * @flags: for future use. - * @freq: center frequence of a channel which should be used to connect. - * @bg_scan_period: period of background scan. * @bssid: BSSID of the BSS to connect to. + * @bssid_hint: recommended AP BSSID for initial connection to the BSS or + * 00:00:00:00:00:00 if not specified. + * @prev_bssid: previous BSSID, if specified (not 00:00:00:00:00:00) indicates + * a request to reassociate. + * @bg_scan_period: period of background scan. + * @flags: one of &enum qlink_sta_connect_flags. + * @ht_capa: HT Capabilities overrides. + * @ht_capa_mask: The bits of ht_capa which are to be used. + * @vht_capa: VHT Capability overrides + * @vht_capa_mask: The bits of vht_capa which are to be used. + * @aen: authentication information. + * @mfp: whether to use management frame protection. * @payload: variable portion of connection request. */ struct qlink_cmd_connect { struct qlink_cmd chdr; - __le32 flags; - __le16 freq; - __le16 bg_scan_period; u8 bssid[ETH_ALEN]; + u8 bssid_hint[ETH_ALEN]; + u8 prev_bssid[ETH_ALEN]; + __le16 bg_scan_period; + __le32 flags; + struct ieee80211_ht_cap ht_capa; + struct ieee80211_ht_cap ht_capa_mask; + struct ieee80211_vht_cap vht_capa; + struct ieee80211_vht_cap vht_capa_mask; + struct qlink_auth_encr aen; + u8 mfp; + u8 pbss; + u8 rsvd[2]; u8 payload[0]; } __packed; @@ -421,15 +472,119 @@ enum qlink_band { }; /** - * struct qlink_cmd_chans_info_get - data for QLINK_CMD_CHANS_INFO_GET command + * struct qlink_cmd_band_info_get - data for QLINK_CMD_BAND_INFO_GET command * - * @band: a PHY band for which channels info is needed, one of @enum qlink_band + * @band: a PHY band for which information is queried, one of @enum qlink_band */ -struct qlink_cmd_chans_info_get { +struct qlink_cmd_band_info_get { struct qlink_cmd chdr; u8 band; } __packed; +/** + * struct qlink_cmd_get_chan_stats - data for QLINK_CMD_CHAN_STATS command + * + * @channel: channel number according to 802.11 17.3.8.3.2 and Annex J + */ +struct qlink_cmd_get_chan_stats { + struct qlink_cmd chdr; + __le16 channel; +} __packed; + +/** + * enum qlink_reg_initiator - Indicates the initiator of a reg domain request + * + * See &enum nl80211_reg_initiator for more info. + */ +enum qlink_reg_initiator { + QLINK_REGDOM_SET_BY_CORE, + QLINK_REGDOM_SET_BY_USER, + QLINK_REGDOM_SET_BY_DRIVER, + QLINK_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum qlink_user_reg_hint_type - type of user regulatory hint + * + * See &enum nl80211_user_reg_hint_type for more info. + */ +enum qlink_user_reg_hint_type { + QLINK_USER_REG_HINT_USER = 0, + QLINK_USER_REG_HINT_CELL_BASE = 1, + QLINK_USER_REG_HINT_INDOOR = 2, +}; + +/** + * struct qlink_cmd_reg_notify - data for QLINK_CMD_REG_NOTIFY command + * + * @alpha2: the ISO / IEC 3166 alpha2 country code. + * @initiator: which entity sent the request, one of &enum qlink_reg_initiator. + * @user_reg_hint_type: type of hint for QLINK_REGDOM_SET_BY_USER request, one + * of &enum qlink_user_reg_hint_type. + */ +struct qlink_cmd_reg_notify { + struct qlink_cmd chdr; + u8 alpha2[2]; + u8 initiator; + u8 user_reg_hint_type; +} __packed; + +/** + * struct qlink_cmd_chan_switch - data for QLINK_CMD_CHAN_SWITCH command + * + * @channel: channel number according to 802.11 17.3.8.3.2 and Annex J + * @radar_required: whether radar detection is required on the new channel + * @block_tx: whether transmissions should be blocked while changing + * @beacon_count: number of beacons until switch + */ +struct qlink_cmd_chan_switch { + struct qlink_cmd chdr; + __le16 channel; + u8 radar_required; + u8 block_tx; + u8 beacon_count; +} __packed; + +/** + * enum qlink_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * + * Refer to &enum nl80211_hidden_ssid + */ +enum qlink_hidden_ssid { + QLINK_HIDDEN_SSID_NOT_IN_USE, + QLINK_HIDDEN_SSID_ZERO_LEN, + QLINK_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * struct qlink_cmd_start_ap - data for QLINK_CMD_START_AP command + * + * @beacon_interval: beacon interval + * @inactivity_timeout: station's inactivity period in seconds + * @dtim_period: DTIM period + * @hidden_ssid: whether to hide the SSID, one of &enum qlink_hidden_ssid + * @smps_mode: SMPS mode + * @ht_required: stations must support HT + * @vht_required: stations must support VHT + * @aen: encryption info + * @info: variable configurations + */ +struct qlink_cmd_start_ap { + struct qlink_cmd chdr; + __le16 beacon_interval; + __le16 inactivity_timeout; + u8 dtim_period; + u8 hidden_ssid; + u8 smps_mode; + u8 p2p_ctwindow; + u8 p2p_opp_ps; + u8 pbss; + u8 ht_required; + u8 vht_required; + struct qlink_auth_encr aen; + u8 info[0]; +} __packed; + /* QLINK Command Responses messages related definitions */ @@ -438,6 +593,7 @@ enum qlink_cmd_result { QLINK_CMD_RESULT_INVALID, QLINK_CMD_RESULT_ENOTSUPP, QLINK_CMD_RESULT_ENOTFOUND, + QLINK_CMD_RESULT_EALREADY, }; /** @@ -474,10 +630,9 @@ struct qlink_resp { * specified WMAC). * @num_tx_chain: Number of transmit chains used by WMAC. * @num_rx_chain: Number of receive chains used by WMAC. - * @vht_cap: VHT capabilities. - * @ht_cap: HT capabilities. + * @vht_cap_mod_mask: mask specifying which VHT capabilities can be altered. + * @ht_cap_mod_mask: mask specifying which HT capabilities can be altered. * @bands_cap: wireless bands WMAC can operate in, bitmap of &enum qlink_band. - * @phymode_cap: PHY modes WMAC can operate in, bitmap of &enum qlink_phy_mode. * @max_ap_assoc_sta: Maximum number of associations supported by WMAC. * @radar_detect_widths: bitmask of channels BW for which WMAC can detect radar. * @var_info: variable-length WMAC info data. @@ -487,16 +642,28 @@ struct qlink_resp_get_mac_info { u8 dev_mac[ETH_ALEN]; u8 num_tx_chain; u8 num_rx_chain; - struct ieee80211_vht_cap vht_cap; - struct ieee80211_ht_cap ht_cap; - u8 bands_cap; - u8 phymode_cap; + struct ieee80211_vht_cap vht_cap_mod_mask; + struct ieee80211_ht_cap ht_cap_mod_mask; __le16 max_ap_assoc_sta; __le16 radar_detect_widths; + u8 bands_cap; + u8 rsvd[1]; u8 var_info[0]; } __packed; /** + * enum qlink_dfs_regions - regulatory DFS regions + * + * Corresponds to &enum nl80211_dfs_regions. + */ +enum qlink_dfs_regions { + QLINK_DFS_UNSET = 0, + QLINK_DFS_FCC = 1, + QLINK_DFS_ETSI = 2, + QLINK_DFS_JP = 3, +}; + +/** * struct qlink_resp_get_hw_info - response for QLINK_CMD_GET_HW_INFO command * * Description of wireless hardware capabilities and features. @@ -504,22 +671,29 @@ struct qlink_resp_get_mac_info { * @fw_ver: wireless hardware firmware version. * @hw_capab: Bitmap of capabilities supported by firmware. * @ql_proto_ver: Version of QLINK protocol used by firmware. - * @country_code: country code ID firmware is configured to. * @num_mac: Number of separate physical radio devices provided by hardware. * @mac_bitmap: Bitmap of MAC IDs that are active and can be used in firmware. * @total_tx_chains: total number of transmit chains used by device. * @total_rx_chains: total number of receive chains. + * @alpha2: country code ID firmware is configured to. + * @n_reg_rules: number of regulatory rules TLVs in variable portion of the + * message. + * @dfs_region: regulatory DFS region, one of @enum qlink_dfs_region. + * @info: variable-length HW info, can contain QTN_TLV_ID_REG_RULE. */ struct qlink_resp_get_hw_info { struct qlink_resp rhdr; __le32 fw_ver; __le32 hw_capab; __le16 ql_proto_ver; - u8 alpha2_code[2]; u8 num_mac; u8 mac_bitmap; u8 total_tx_chain; u8 total_rx_chain; + u8 alpha2[2]; + u8 n_reg_rules; + u8 dfs_region; + u8 info[0]; } __packed; /** @@ -550,17 +724,19 @@ struct qlink_resp_get_sta_info { } __packed; /** - * struct qlink_resp_get_chan_info - response for QLINK_CMD_CHANS_INFO_GET cmd + * struct qlink_resp_band_info_get - response for QLINK_CMD_BAND_INFO_GET cmd * - * @band: frequency band to which channels belong to, one of @enum qlink_band. - * @num_chans: total number of channels info data contained in reply data. - * @info: variable-length channels info. + * @band: frequency band that the response describes, one of @enum qlink_band. + * @num_chans: total number of channels info TLVs contained in reply. + * @num_bitrates: total number of bitrate TLVs contained in reply. + * @info: variable-length info portion. */ -struct qlink_resp_get_chan_info { +struct qlink_resp_band_info_get { struct qlink_resp rhdr; u8 band; u8 num_chans; - u8 rsvd[2]; + u8 num_bitrates; + u8 rsvd[1]; u8 info[0]; } __packed; @@ -574,6 +750,26 @@ struct qlink_resp_phy_params { u8 info[0]; } __packed; +/** + * struct qlink_resp_get_chan_stats - response for QLINK_CMD_CHAN_STATS cmd + * + * @info: variable-length channel info. + */ +struct qlink_resp_get_chan_stats { + struct qlink_cmd rhdr; + u8 info[0]; +} __packed; + +/** + * struct qlink_resp_channel_get - response for QLINK_CMD_CHAN_GET command + * + * @chan: definition of current operating channel. + */ +struct qlink_resp_channel_get { + struct qlink_resp rhdr; + struct qlink_chandef chan; +} __packed; + /* QLINK Events messages related definitions */ @@ -585,6 +781,7 @@ enum qlink_event_type { QLINK_EVENT_SCAN_COMPLETE = 0x0025, QLINK_EVENT_BSS_JOIN = 0x0026, QLINK_EVENT_BSS_LEAVE = 0x0027, + QLINK_EVENT_FREQ_CHANGE = 0x0028, }; /** @@ -651,7 +848,17 @@ struct qlink_event_bss_join { */ struct qlink_event_bss_leave { struct qlink_event ehdr; - u16 reason; + __le16 reason; +} __packed; + +/** + * struct qlink_event_freq_change - data for QLINK_EVENT_FREQ_CHANGE event + * + * @chan: new operating channel definition + */ +struct qlink_event_freq_change { + struct qlink_event ehdr; + struct qlink_chandef chan; } __packed; enum qlink_rxmgmt_flags { @@ -674,12 +881,6 @@ struct qlink_event_rxmgmt { u8 frame_data[0]; } __packed; -enum qlink_frame_type { - QLINK_BSS_FTYPE_UNKNOWN, - QLINK_BSS_FTYPE_BEACON, - QLINK_BSS_FTYPE_PRESP, -}; - /** * struct qlink_event_scan_result - data for QLINK_EVENT_SCAN_RESULTS event * @@ -689,7 +890,6 @@ enum qlink_frame_type { * @capab: capabilities field. * @bintval: beacon interval announced by discovered BSS. * @signal: signal strength. - * @frame_type: frame type used to get scan result, see &enum qlink_frame_type. * @bssid: BSSID announced by discovered BSS. * @ssid_len: length of SSID announced by BSS. * @ssid: SSID announced by discovered BSS. @@ -702,10 +902,10 @@ struct qlink_event_scan_result { __le16 capab; __le16 bintval; s8 signal; - u8 frame_type; - u8 bssid[ETH_ALEN]; u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bssid[ETH_ALEN]; + u8 rsvd[2]; u8 payload[0]; } __packed; @@ -739,17 +939,17 @@ enum qlink_tlv_id { QTN_TLV_ID_RTS_THRESH = 0x0202, QTN_TLV_ID_SRETRY_LIMIT = 0x0203, QTN_TLV_ID_LRETRY_LIMIT = 0x0204, - QTN_TLV_ID_BCN_PERIOD = 0x0205, - QTN_TLV_ID_DTIM = 0x0206, + QTN_TLV_ID_REG_RULE = 0x0207, QTN_TLV_ID_CHANNEL = 0x020F, + QTN_TLV_ID_CHANDEF = 0x0210, QTN_TLV_ID_COVERAGE_CLASS = 0x0213, QTN_TLV_ID_IFACE_LIMIT = 0x0214, QTN_TLV_ID_NUM_IFACE_COMB = 0x0215, + QTN_TLV_ID_CHANNEL_STATS = 0x0216, QTN_TLV_ID_STA_BASIC_COUNTERS = 0x0300, QTN_TLV_ID_STA_GENERIC_INFO = 0x0301, QTN_TLV_ID_KEY = 0x0302, QTN_TLV_ID_SEQ = 0x0303, - QTN_TLV_ID_CRYPTO = 0x0304, QTN_TLV_ID_IE_SET = 0x0305, }; @@ -761,7 +961,7 @@ struct qlink_tlv_hdr { struct qlink_iface_limit { __le16 max_num; - __le16 type_mask; + __le16 type; } __packed; struct qlink_iface_comb_num { @@ -844,12 +1044,54 @@ struct qlink_tlv_cclass { u8 cclass; } __packed; -enum qlink_dfs_state { - QLINK_DFS_USABLE, - QLINK_DFS_UNAVAILABLE, - QLINK_DFS_AVAILABLE, +/** + * enum qlink_reg_rule_flags - regulatory rule flags + * + * See description of &enum nl80211_reg_rule_flags + */ +enum qlink_reg_rule_flags { + QLINK_RRF_NO_OFDM = BIT(0), + QLINK_RRF_NO_CCK = BIT(1), + QLINK_RRF_NO_INDOOR = BIT(2), + QLINK_RRF_NO_OUTDOOR = BIT(3), + QLINK_RRF_DFS = BIT(4), + QLINK_RRF_PTP_ONLY = BIT(5), + QLINK_RRF_PTMP_ONLY = BIT(6), + QLINK_RRF_NO_IR = BIT(7), + QLINK_RRF_AUTO_BW = BIT(8), + QLINK_RRF_IR_CONCURRENT = BIT(9), + QLINK_RRF_NO_HT40MINUS = BIT(10), + QLINK_RRF_NO_HT40PLUS = BIT(11), + QLINK_RRF_NO_80MHZ = BIT(12), + QLINK_RRF_NO_160MHZ = BIT(13), }; +/** + * struct qlink_tlv_reg_rule - data for QTN_TLV_ID_REG_RULE TLV + * + * Regulatory rule description. + * + * @start_freq_khz: start frequency of the range the rule is attributed to. + * @end_freq_khz: end frequency of the range the rule is attributed to. + * @max_bandwidth_khz: max bandwidth that channels in specified range can be + * configured to. + * @max_antenna_gain: max antenna gain that can be used in the specified + * frequency range, dBi. + * @max_eirp: maximum EIRP. + * @flags: regulatory rule flags in &enum qlink_reg_rule_flags. + * @dfs_cac_ms: DFS CAC period. + */ +struct qlink_tlv_reg_rule { + struct qlink_tlv_hdr hdr; + __le32 start_freq_khz; + __le32 end_freq_khz; + __le32 max_bandwidth_khz; + __le32 max_antenna_gain; + __le32 max_eirp; + __le32 flags; + __le32 dfs_cac_ms; +} __packed; + enum qlink_channel_flags { QLINK_CHAN_DISABLED = BIT(0), QLINK_CHAN_NO_IR = BIT(1), @@ -865,6 +1107,12 @@ enum qlink_channel_flags { QLINK_CHAN_NO_10MHZ = BIT(12), }; +enum qlink_dfs_state { + QLINK_DFS_USABLE, + QLINK_DFS_UNAVAILABLE, + QLINK_DFS_AVAILABLE, +}; + struct qlink_tlv_channel { struct qlink_tlv_hdr hdr; __le16 hw_value; @@ -880,22 +1128,52 @@ struct qlink_tlv_channel { u8 rsvd[2]; } __packed; -#define QLINK_MAX_NR_CIPHER_SUITES 5 -#define QLINK_MAX_NR_AKM_SUITES 2 +/** + * struct qlink_tlv_chandef - data for QTN_TLV_ID_CHANDEF TLV + * + * Channel definition. + * + * @chan: channel definition data. + */ +struct qlink_tlv_chandef { + struct qlink_tlv_hdr hdr; + struct qlink_chandef chan; +} __packed; -struct qlink_auth_encr { - __le32 wpa_versions; - __le32 cipher_group; - __le32 n_ciphers_pairwise; - __le32 ciphers_pairwise[QLINK_MAX_NR_CIPHER_SUITES]; - __le32 n_akm_suites; - __le32 akm_suites[QLINK_MAX_NR_AKM_SUITES]; - __le16 control_port_ethertype; - u8 auth_type; - u8 privacy; - u8 mfp; - u8 control_port; - u8 control_port_no_encrypt; +enum qlink_ie_set_type { + QLINK_IE_SET_UNKNOWN, + QLINK_IE_SET_ASSOC_REQ, + QLINK_IE_SET_ASSOC_RESP, + QLINK_IE_SET_PROBE_REQ, + QLINK_IE_SET_SCAN, + QLINK_IE_SET_BEACON_HEAD, + QLINK_IE_SET_BEACON_TAIL, + QLINK_IE_SET_BEACON_IES, + QLINK_IE_SET_PROBE_RESP, + QLINK_IE_SET_PROBE_RESP_IES, +}; + +/** + * struct qlink_tlv_ie_set - data for QTN_TLV_ID_IE_SET + * + * @type: type of MGMT frame IEs belong to, one of &enum qlink_ie_set_type. + * @flags: for future use. + * @ie_data: IEs data. + */ +struct qlink_tlv_ie_set { + struct qlink_tlv_hdr hdr; + u8 type; + u8 flags; + u8 ie_data[0]; +} __packed; + +struct qlink_chan_stats { + __le32 chan_num; + __le32 cca_tx; + __le32 cca_rx; + __le32 cca_busy; + __le32 cca_try; + s8 chan_noise; } __packed; #endif /* _QTN_QLINK_H_ */ diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c index 49ae652ad9a3..61d999affb09 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.c @@ -17,24 +17,30 @@ #include "qlink_util.h" -u16 qlink_iface_type_mask_to_nl(u16 qlink_mask) +u16 qlink_iface_type_to_nl_mask(u16 qlink_type) { u16 result = 0; - if (qlink_mask & QLINK_IFTYPE_AP) + switch (qlink_type) { + case QLINK_IFTYPE_AP: result |= BIT(NL80211_IFTYPE_AP); - - if (qlink_mask & QLINK_IFTYPE_STATION) + break; + case QLINK_IFTYPE_STATION: result |= BIT(NL80211_IFTYPE_STATION); - - if (qlink_mask & QLINK_IFTYPE_ADHOC) + break; + case QLINK_IFTYPE_ADHOC: result |= BIT(NL80211_IFTYPE_ADHOC); - - if (qlink_mask & QLINK_IFTYPE_MONITOR) + break; + case QLINK_IFTYPE_MONITOR: result |= BIT(NL80211_IFTYPE_MONITOR); - - if (qlink_mask & QLINK_IFTYPE_WDS) + break; + case QLINK_IFTYPE_WDS: result |= BIT(NL80211_IFTYPE_WDS); + break; + case QLINK_IFTYPE_AP_VLAN: + result |= BIT(NL80211_IFTYPE_AP_VLAN); + break; + } return result; } @@ -43,29 +49,126 @@ u8 qlink_chan_width_mask_to_nl(u16 qlink_mask) { u8 result = 0; - if (qlink_mask & QLINK_CHAN_WIDTH_5) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_5)) result |= BIT(NL80211_CHAN_WIDTH_5); - if (qlink_mask & QLINK_CHAN_WIDTH_10) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_10)) result |= BIT(NL80211_CHAN_WIDTH_10); - if (qlink_mask & QLINK_CHAN_WIDTH_20_NOHT) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_20_NOHT)) result |= BIT(NL80211_CHAN_WIDTH_20_NOHT); - if (qlink_mask & QLINK_CHAN_WIDTH_20) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_20)) result |= BIT(NL80211_CHAN_WIDTH_20); - if (qlink_mask & QLINK_CHAN_WIDTH_40) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_40)) result |= BIT(NL80211_CHAN_WIDTH_40); - if (qlink_mask & QLINK_CHAN_WIDTH_80) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_80)) result |= BIT(NL80211_CHAN_WIDTH_80); - if (qlink_mask & QLINK_CHAN_WIDTH_80P80) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_80P80)) result |= BIT(NL80211_CHAN_WIDTH_80P80); - if (qlink_mask & QLINK_CHAN_WIDTH_160) + if (qlink_mask & BIT(QLINK_CHAN_WIDTH_160)) result |= BIT(NL80211_CHAN_WIDTH_160); return result; } + +static enum nl80211_chan_width qlink_chanwidth_to_nl(u8 qlw) +{ + switch (qlw) { + case QLINK_CHAN_WIDTH_20_NOHT: + return NL80211_CHAN_WIDTH_20_NOHT; + case QLINK_CHAN_WIDTH_20: + return NL80211_CHAN_WIDTH_20; + case QLINK_CHAN_WIDTH_40: + return NL80211_CHAN_WIDTH_40; + case QLINK_CHAN_WIDTH_80: + return NL80211_CHAN_WIDTH_80; + case QLINK_CHAN_WIDTH_80P80: + return NL80211_CHAN_WIDTH_80P80; + case QLINK_CHAN_WIDTH_160: + return NL80211_CHAN_WIDTH_160; + case QLINK_CHAN_WIDTH_5: + return NL80211_CHAN_WIDTH_5; + case QLINK_CHAN_WIDTH_10: + return NL80211_CHAN_WIDTH_10; + default: + return -1; + } +} + +void qlink_chandef_q2cfg(struct wiphy *wiphy, + const struct qlink_chandef *qch, + struct cfg80211_chan_def *chdef) +{ + chdef->center_freq1 = le16_to_cpu(qch->center_freq1); + chdef->center_freq2 = le16_to_cpu(qch->center_freq2); + chdef->width = qlink_chanwidth_to_nl(qch->width); + + switch (chdef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + chdef->chan = ieee80211_get_channel(wiphy, chdef->center_freq1); + break; + case NL80211_CHAN_WIDTH_40: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + chdef->chan = ieee80211_get_channel(wiphy, + chdef->center_freq1 - 10); + break; + default: + chdef->chan = NULL; + break; + } +} + +static u8 qlink_chanwidth_nl_to_qlink(enum nl80211_chan_width nlwidth) +{ + switch (nlwidth) { + case NL80211_CHAN_WIDTH_20_NOHT: + return QLINK_CHAN_WIDTH_20_NOHT; + case NL80211_CHAN_WIDTH_20: + return QLINK_CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return QLINK_CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return QLINK_CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_80P80: + return QLINK_CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_160: + return QLINK_CHAN_WIDTH_160; + case NL80211_CHAN_WIDTH_5: + return QLINK_CHAN_WIDTH_5; + case NL80211_CHAN_WIDTH_10: + return QLINK_CHAN_WIDTH_10; + default: + return -1; + } +} + +void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef, + struct qlink_chandef *qch) +{ + qch->center_freq1 = cpu_to_le16(chdef->center_freq1); + qch->center_freq2 = cpu_to_le16(chdef->center_freq2); + qch->width = qlink_chanwidth_nl_to_qlink(chdef->width); +} + +enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val) +{ + switch (nl_val) { + case NL80211_HIDDEN_SSID_ZERO_LEN: + return QLINK_HIDDEN_SSID_ZERO_LEN; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + return QLINK_HIDDEN_SSID_ZERO_CONTENTS; + case NL80211_HIDDEN_SSID_NOT_IN_USE: + default: + return QLINK_HIDDEN_SSID_NOT_IN_USE; + } +} diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h index 90d7d09a6c63..260383d6d109 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink_util.h @@ -19,17 +19,10 @@ #include <linux/types.h> #include <linux/skbuff.h> +#include <net/cfg80211.h> #include "qlink.h" -static inline void qtnf_cmd_skb_put_action(struct sk_buff *skb, u16 action) -{ - __le16 *buf_ptr; - - buf_ptr = skb_put(skb, sizeof(action)); - *buf_ptr = cpu_to_le16(action); -} - static inline void qtnf_cmd_skb_put_buffer(struct sk_buff *skb, const u8 *buf_src, size_t len) { @@ -68,7 +61,13 @@ static inline void qtnf_cmd_skb_put_tlv_u16(struct sk_buff *skb, memcpy(hdr->val, &tmp, sizeof(tmp)); } -u16 qlink_iface_type_mask_to_nl(u16 qlink_mask); +u16 qlink_iface_type_to_nl_mask(u16 qlink_type); u8 qlink_chan_width_mask_to_nl(u16 qlink_mask); +void qlink_chandef_q2cfg(struct wiphy *wiphy, + const struct qlink_chandef *qch, + struct cfg80211_chan_def *chdef); +void qlink_chandef_cfg2q(const struct cfg80211_chan_def *chdef, + struct qlink_chandef *qch); +enum qlink_hidden_ssid qlink_hidden_ssid_nl2q(enum nl80211_hidden_ssid nl_val); #endif /* _QTN_FMAC_QLINK_UTIL_H_ */ |

