wifi: mt76: mt7996: fix TWT issues
authorPeter Chiu <chui-hao.chiu@mediatek.com>
Fri, 26 Jan 2024 09:09:13 +0000 (17:09 +0800)
committerFelix Fietkau <nbd@nbd.name>
Thu, 22 Feb 2024 08:55:18 +0000 (09:55 +0100)
This patch fixes the following TWT issues:
- Change table_mask to u16 to support up to 16 TWT stations
- Reject TWT flows for duplicated establishment
- Fix possible unaligned pointer
- Remove unsupported TWT_CONTROL_WAKE_DUR_UNIT flag
- The minimum TWT duration supported by mt7996 chipsets is 64. Reply
  with TWT_SETUP_CMD_DICTATE if the min_twt_dur is smaller than 64

Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Signed-off-by: Peter Chiu <chui-hao.chiu@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7996/mac.c
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h

index a8414fbb07c82c3f0c02e3162e4eccdf00395d66..63d34844c1223b0383983d94e816982b7855cbe3 100644 (file)
@@ -2530,6 +2530,34 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt)
        return 0;
 }
 
+static bool
+mt7996_mac_twt_param_equal(struct mt7996_sta *msta,
+                          struct ieee80211_twt_params *twt_agrt)
+{
+       u16 type = le16_to_cpu(twt_agrt->req_type);
+       u8 exp;
+       int i;
+
+       exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, type);
+       for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) {
+               struct mt7996_twt_flow *f;
+
+               if (!(msta->twt.flowid_mask & BIT(i)))
+                       continue;
+
+               f = &msta->twt.flow[i];
+               if (f->duration == twt_agrt->min_twt_dur &&
+                   f->mantissa == twt_agrt->mantissa &&
+                   f->exp == exp &&
+                   f->protection == !!(type & IEEE80211_TWT_REQTYPE_PROTECTION) &&
+                   f->flowtype == !!(type & IEEE80211_TWT_REQTYPE_FLOWTYPE) &&
+                   f->trigger == !!(type & IEEE80211_TWT_REQTYPE_TRIGGER))
+                       return true;
+       }
+
+       return false;
+}
+
 void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
                              struct ieee80211_sta *sta,
                              struct ieee80211_twt_setup *twt)
@@ -2541,8 +2569,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
        enum ieee80211_twt_setup_cmd sta_setup_cmd;
        struct mt7996_dev *dev = mt7996_hw_dev(hw);
        struct mt7996_twt_flow *flow;
-       int flowid, table_id;
-       u8 exp;
+       u8 flowid, table_id, exp;
 
        if (mt7996_mac_check_twt_req(twt))
                goto out;
@@ -2555,9 +2582,19 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
        if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow))
                goto unlock;
 
+       if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) {
+               setup_cmd = TWT_SETUP_CMD_DICTATE;
+               twt_agrt->min_twt_dur = MT7996_MIN_TWT_DUR;
+               goto unlock;
+       }
+
+       if (mt7996_mac_twt_param_equal(msta, twt_agrt))
+               goto unlock;
+
        flowid = ffs(~msta->twt.flowid_mask) - 1;
-       le16p_replace_bits(&twt_agrt->req_type, flowid,
-                          IEEE80211_TWT_REQTYPE_FLOWID);
+       twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID);
+       twt_agrt->req_type |= le16_encode_bits(flowid,
+                                              IEEE80211_TWT_REQTYPE_FLOWID);
 
        table_id = ffs(~dev->twt.table_mask) - 1;
        exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type);
@@ -2604,10 +2641,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
 unlock:
        mutex_unlock(&dev->mt76.mutex);
 out:
-       le16p_replace_bits(&twt_agrt->req_type, setup_cmd,
-                          IEEE80211_TWT_REQTYPE_SETUP_CMD);
-       twt->control = (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) |
-                      (twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED);
+       twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_SETUP_CMD);
+       twt_agrt->req_type |=
+               le16_encode_bits(setup_cmd, IEEE80211_TWT_REQTYPE_SETUP_CMD);
+       twt->control = twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED;
 }
 
 void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
index bc73bcb47bf02c86ce6e3e98320fb6491e7f1cc8..8154ad37827f0871a9793df4a7f19a20b4fb8fe7 100644 (file)
@@ -53,6 +53,7 @@
 
 #define MT7996_MAX_TWT_AGRT            16
 #define MT7996_MAX_STA_TWT_AGRT                8
+#define MT7996_MIN_TWT_DUR             64
 #define MT7996_MAX_QUEUE               (__MT_RXQ_MAX + __MT_MCUQ_MAX + 3)
 
 /* NOTE: used to map mt76_rates. idx may change if firmware expands table */
@@ -320,7 +321,7 @@ struct mt7996_dev {
        struct rchan *relay_fwlog;
 
        struct {
-               u8 table_mask;
+               u16 table_mask;
                u8 n_agrt;
        } twt;