summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/mediatek/mt76/mac80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mac80211.c')
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c273
1 files changed, 223 insertions, 50 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index ec9efb79985f..96018fd65779 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1,17 +1,6 @@
+// SPDX-License-Identifier: ISC
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/of.h>
#include "mt76.h"
@@ -116,7 +105,15 @@ static int mt76_led_init(struct mt76_dev *dev)
dev->led_al = of_property_read_bool(np, "led-active-low");
}
- return devm_led_classdev_register(dev->dev, &dev->led_cdev);
+ return led_classdev_register(dev->dev, &dev->led_cdev);
+}
+
+static void mt76_led_cleanup(struct mt76_dev *dev)
+{
+ if (!dev->led_cdev.brightness_set && !dev->led_cdev.blink_set)
+ return;
+
+ led_classdev_unregister(&dev->led_cdev);
}
static void mt76_init_stream_cap(struct mt76_dev *dev,
@@ -191,6 +188,7 @@ mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
sband->bitrates = rates;
sband->n_bitrates = n_rates;
dev->chandef.chan = &sband->channels[0];
+ dev->chan_state = &msband->chan[0];
ht_cap = &sband->ht_cap;
ht_cap->ht_supported = true;
@@ -294,6 +292,8 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
init_waitqueue_head(&dev->tx_wait);
skb_queue_head_init(&dev->status_list);
+ tasklet_init(&dev->tx_tasklet, mt76_tx_tasklet, (unsigned long)dev);
+
return dev;
}
EXPORT_SYMBOL_GPL(mt76_alloc_device);
@@ -315,6 +315,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
wiphy->available_antennas_tx = dev->antenna_mask;
wiphy->available_antennas_rx = dev->antenna_mask;
@@ -336,8 +337,16 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
ieee80211_hw_set(hw, AP_LINK_PS);
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
+ ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_ADHOC);
if (dev->cap.has_2ghz) {
ret = mt76_init_sband_2g(dev, rates, n_rates);
@@ -369,6 +378,8 @@ void mt76_unregister_device(struct mt76_dev *dev)
{
struct ieee80211_hw *hw = dev->hw;
+ if (IS_ENABLED(CONFIG_MT76_LEDS))
+ mt76_led_cleanup(dev);
mt76_tx_status_check(dev, NULL, true);
ieee80211_unregister_hw(hw);
}
@@ -407,33 +418,64 @@ bool mt76_has_tx_pending(struct mt76_dev *dev)
}
EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
+static struct mt76_channel_state *
+mt76_channel_state(struct mt76_dev *dev, struct ieee80211_channel *c)
+{
+ struct mt76_sband *msband;
+ int idx;
+
+ if (c->band == NL80211_BAND_2GHZ)
+ msband = &dev->sband_2g;
+ else
+ msband = &dev->sband_5g;
+
+ idx = c - &msband->sband.channels[0];
+ return &msband->chan[idx];
+}
+
+void mt76_update_survey(struct mt76_dev *dev)
+{
+ struct mt76_channel_state *state = dev->chan_state;
+ ktime_t cur_time;
+
+ if (!test_bit(MT76_STATE_RUNNING, &dev->state))
+ return;
+
+ if (dev->drv->update_survey)
+ dev->drv->update_survey(dev);
+
+ cur_time = ktime_get_boottime();
+ state->cc_active += ktime_to_us(ktime_sub(cur_time,
+ dev->survey_time));
+ dev->survey_time = cur_time;
+
+ if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME) {
+ spin_lock_bh(&dev->cc_lock);
+ state->cc_bss_rx += dev->cur_cc_bss_rx;
+ dev->cur_cc_bss_rx = 0;
+ spin_unlock_bh(&dev->cc_lock);
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_update_survey);
+
void mt76_set_channel(struct mt76_dev *dev)
{
struct ieee80211_hw *hw = dev->hw;
struct cfg80211_chan_def *chandef = &hw->conf.chandef;
- struct mt76_channel_state *state;
bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
int timeout = HZ / 5;
- if (offchannel)
- set_bit(MT76_OFFCHANNEL, &dev->state);
- else
- clear_bit(MT76_OFFCHANNEL, &dev->state);
-
wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(dev), timeout);
-
- if (dev->drv->update_survey)
- dev->drv->update_survey(dev);
+ mt76_update_survey(dev);
dev->chandef = *chandef;
+ dev->chan_state = mt76_channel_state(dev, chandef->chan);
if (!offchannel)
dev->main_chan = chandef->chan;
- if (chandef->chan != dev->main_chan) {
- state = mt76_channel_state(dev, chandef->chan);
- memset(state, 0, sizeof(*state));
- }
+ if (chandef->chan != dev->main_chan)
+ memset(dev->chan_state, 0, sizeof(*dev->chan_state));
}
EXPORT_SYMBOL_GPL(mt76_set_channel);
@@ -446,8 +488,9 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct mt76_channel_state *state;
int ret = 0;
+ mutex_lock(&dev->mutex);
if (idx == 0 && dev->drv->update_survey)
- dev->drv->update_survey(dev);
+ mt76_update_survey(dev);
sband = &dev->sband_2g;
if (idx >= sband->sband.n_channels) {
@@ -455,8 +498,10 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
sband = &dev->sband_5g;
}
- if (idx >= sband->sband.n_channels)
- return -ENOENT;
+ if (idx >= sband->sband.n_channels) {
+ ret = -ENOENT;
+ goto out;
+ }
chan = &sband->sband.channels[idx];
state = mt76_channel_state(dev, chan);
@@ -464,14 +509,26 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
memset(survey, 0, sizeof(*survey));
survey->channel = chan;
survey->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY;
- if (chan == dev->main_chan)
+ survey->filled |= dev->drv->survey_flags;
+ if (chan == dev->main_chan) {
survey->filled |= SURVEY_INFO_IN_USE;
- spin_lock_bh(&dev->cc_lock);
- survey->time = div_u64(state->cc_active, 1000);
+ if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME)
+ survey->filled |= SURVEY_INFO_TIME_BSS_RX;
+ }
+
survey->time_busy = div_u64(state->cc_busy, 1000);
+ survey->time_rx = div_u64(state->cc_rx, 1000);
+ survey->time = div_u64(state->cc_active, 1000);
+
+ spin_lock_bh(&dev->cc_lock);
+ survey->time_bss_rx = div_u64(state->cc_bss_rx, 1000);
+ survey->time_tx = div_u64(state->cc_tx, 1000);
spin_unlock_bh(&dev->cc_lock);
+out:
+ mutex_unlock(&dev->mutex);
+
return ret;
}
EXPORT_SYMBOL_GPL(mt76_get_survey);
@@ -487,9 +544,10 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
if (!key)
return;
- if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
- wcid->rx_check_pn = true;
+ if (key->cipher != WLAN_CIPHER_SUITE_CCMP)
+ return;
+ wcid->rx_check_pn = true;
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
ieee80211_get_key_rx_seq(key, i, &seq);
memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn));
@@ -497,12 +555,12 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
}
EXPORT_SYMBOL(mt76_wcid_key_setup);
-struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
+static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct mt76_rx_status mstat;
- mstat = *((struct mt76_rx_status *) skb->cb);
+ mstat = *((struct mt76_rx_status *)skb->cb);
memset(status, 0, sizeof(*status));
status->flag = mstat.flag;
@@ -515,19 +573,21 @@ struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
status->band = mstat.band;
status->signal = mstat.signal;
status->chains = mstat.chains;
+ status->ampdu_reference = mstat.ampdu_ref;
BUILD_BUG_ON(sizeof(mstat) > sizeof(skb->cb));
- BUILD_BUG_ON(sizeof(status->chain_signal) != sizeof(mstat.chain_signal));
- memcpy(status->chain_signal, mstat.chain_signal, sizeof(mstat.chain_signal));
+ BUILD_BUG_ON(sizeof(status->chain_signal) !=
+ sizeof(mstat.chain_signal));
+ memcpy(status->chain_signal, mstat.chain_signal,
+ sizeof(mstat.chain_signal));
return wcid_to_sta(mstat.wcid);
}
-EXPORT_SYMBOL(mt76_rx_convert);
static int
mt76_check_ccmp_pn(struct sk_buff *skb)
{
- struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
struct mt76_wcid *wcid = status->wcid;
struct ieee80211_hdr *hdr;
int ret;
@@ -543,7 +603,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
* Validate the first fragment both here and in mac80211
* All further fragments will be validated by mac80211 only.
*/
- hdr = (struct ieee80211_hdr *) skb->data;
+ hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_frag(hdr) &&
!ieee80211_is_first_frag(hdr->frame_control))
return 0;
@@ -564,10 +624,88 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
}
static void
+mt76_airtime_report(struct mt76_dev *dev, struct mt76_rx_status *status,
+ int len)
+{
+ struct mt76_wcid *wcid = status->wcid;
+ struct ieee80211_sta *sta;
+ u32 airtime;
+
+ airtime = mt76_calc_rx_airtime(dev, status, len);
+ spin_lock(&dev->cc_lock);
+ dev->cur_cc_bss_rx += airtime;
+ spin_unlock(&dev->cc_lock);
+
+ if (!wcid || !wcid->sta)
+ return;
+
+ sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+ ieee80211_sta_register_airtime(sta, status->tid, 0, airtime);
+}
+
+static void
+mt76_airtime_flush_ampdu(struct mt76_dev *dev)
+{
+ struct mt76_wcid *wcid;
+ int wcid_idx;
+
+ if (!dev->rx_ampdu_len)
+ return;
+
+ wcid_idx = dev->rx_ampdu_status.wcid_idx;
+ if (wcid_idx < ARRAY_SIZE(dev->wcid))
+ wcid = rcu_dereference(dev->wcid[wcid_idx]);
+ else
+ wcid = NULL;
+ dev->rx_ampdu_status.wcid = wcid;
+
+ mt76_airtime_report(dev, &dev->rx_ampdu_status, dev->rx_ampdu_len);
+
+ dev->rx_ampdu_len = 0;
+ dev->rx_ampdu_ref = 0;
+}
+
+static void
+mt76_airtime_check(struct mt76_dev *dev, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct mt76_wcid *wcid = status->wcid;
+
+ if (!(dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME))
+ return;
+
+ if (!wcid || !wcid->sta) {
+ if (!ether_addr_equal(hdr->addr1, dev->macaddr))
+ return;
+
+ wcid = NULL;
+ }
+
+ if (!(status->flag & RX_FLAG_AMPDU_DETAILS) ||
+ status->ampdu_ref != dev->rx_ampdu_ref)
+ mt76_airtime_flush_ampdu(dev);
+
+ if (status->flag & RX_FLAG_AMPDU_DETAILS) {
+ if (!dev->rx_ampdu_len ||
+ status->ampdu_ref != dev->rx_ampdu_ref) {
+ dev->rx_ampdu_status = *status;
+ dev->rx_ampdu_status.wcid_idx = wcid ? wcid->idx : 0xff;
+ dev->rx_ampdu_ref = status->ampdu_ref;
+ }
+
+ dev->rx_ampdu_len += skb->len;
+ return;
+ }
+
+ mt76_airtime_report(dev, status, skb->len);
+}
+
+static void
mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
{
- struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_sta *sta;
struct mt76_wcid *wcid = status->wcid;
bool ps;
@@ -576,13 +714,15 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
if (ieee80211_is_pspoll(hdr->frame_control) && !wcid) {
sta = ieee80211_find_sta_by_ifaddr(dev->hw, hdr->addr2, NULL);
if (sta)
- wcid = status->wcid = (struct mt76_wcid *) sta->drv_priv;
+ wcid = status->wcid = (struct mt76_wcid *)sta->drv_priv;
}
+ mt76_airtime_check(dev, skb);
+
if (!wcid || !wcid->sta)
return;
- sta = container_of((void *) wcid, struct ieee80211_sta, drv_priv);
+ sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
if (status->signal <= 0)
ewma_signal_add(&wcid->rssi, -status->signal);
@@ -598,8 +738,8 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
}
if (ieee80211_has_morefrags(hdr->frame_control) ||
- !(ieee80211_is_mgmt(hdr->frame_control) ||
- ieee80211_is_data(hdr->frame_control)))
+ !(ieee80211_is_mgmt(hdr->frame_control) ||
+ ieee80211_is_data(hdr->frame_control)))
return;
ps = ieee80211_has_pm(hdr->frame_control);
@@ -628,7 +768,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
if (!sta->txq[i])
continue;
- mtxq = (struct mt76_txq *) sta->txq[i]->drv_priv;
+ mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
if (!skb_queue_empty(&mtxq->retry_q))
ieee80211_schedule_txq(dev->hw, sta->txq[i]);
}
@@ -714,6 +854,9 @@ void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
rcu_assign_pointer(dev->wcid[idx], NULL);
synchronize_rcu();
+ for (i = 0; i < ARRAY_SIZE(wcid->aggr); i++)
+ mt76_rx_aggr_stop(dev, wcid, i);
+
if (dev->drv->sta_remove)
dev->drv->sta_remove(dev, vif, sta);
@@ -750,7 +893,7 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
dev->drv->sta_assoc(dev, vif, sta);
if (old_state == IEEE80211_STA_NONE &&
- new_state == IEEE80211_STA_NOTEXIST)
+ new_state == IEEE80211_STA_NOTEXIST)
mt76_sta_remove(dev, vif, sta);
return 0;
@@ -790,7 +933,7 @@ static void
__mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{
if (vif->csa_active && ieee80211_csa_is_complete(vif))
- ieee80211_csa_finish(vif);
+ ieee80211_csa_finish(vif);
}
void mt76_csa_finish(struct mt76_dev *dev)
@@ -878,3 +1021,33 @@ int mt76_get_rate(struct mt76_dev *dev,
return 0;
}
EXPORT_SYMBOL_GPL(mt76_get_rate);
+
+void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ const u8 *mac)
+{
+ struct mt76_dev *dev = hw->priv;
+
+ set_bit(MT76_SCANNING, &dev->state);
+}
+EXPORT_SYMBOL_GPL(mt76_sw_scan);
+
+void mt76_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct mt76_dev *dev = hw->priv;
+
+ clear_bit(MT76_SCANNING, &dev->state);
+}
+EXPORT_SYMBOL_GPL(mt76_sw_scan_complete);
+
+int mt76_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+ struct mt76_dev *dev = hw->priv;
+
+ mutex_lock(&dev->mutex);
+ *tx_ant = dev->antenna_mask;
+ *rx_ant = dev->antenna_mask;
+ mutex_unlock(&dev->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_get_antenna);
OpenPOWER on IntegriCloud