OSDN Git Service

mt76: fix beacon timer drift
authorFelix Fietkau <nbd@nbd.name>
Thu, 21 Jun 2018 09:17:53 +0000 (11:17 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Wed, 27 Jun 2018 16:14:46 +0000 (19:14 +0300)
The beacon timer drifts by 1 microsecond every TBTT. After 20 minutes
with a beacon interval of 100, the drift will be almost 12 ms, enough to
cause weird issues for devices in powersave mode.

Since the beacon timer is configured in units of 1/16 TU (64 us), we
need to adjust it once every 64 beacons and only for one beacon.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mediatek/mt76/mt76x2.h
drivers/net/wireless/mediatek/mt76/mt76x2_main.c
drivers/net/wireless/mediatek/mt76/mt76x2_tx.c

index dc12bbd..06ca5a7 100644 (file)
@@ -120,10 +120,13 @@ struct mt76x2_dev {
        u8 beacon_mask;
        u8 beacon_data_mask;
 
-       u32 rxfilter;
+       u8 tbtt_count;
+       u16 beacon_int;
 
        u16 chainmask;
 
+       u32 rxfilter;
+
        struct mt76x2_calibration cal;
 
        s8 target_power;
index ce90ff9..e4e41fa 100644 (file)
@@ -238,10 +238,13 @@ mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        if (changed & BSS_CHANGED_BSSID)
                mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid);
 
-       if (changed & BSS_CHANGED_BEACON_INT)
+       if (changed & BSS_CHANGED_BEACON_INT) {
                mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
                               MT_BEACON_TIME_CFG_INTVAL,
                               info->beacon_int << 4);
+               dev->beacon_int = info->beacon_int;
+               dev->tbtt_count = 0;
+       }
 
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
                tasklet_disable(&dev->pre_tbtt_tasklet);
index e46eafc..560376d 100644 (file)
@@ -218,6 +218,37 @@ mt76x2_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif)
        data->tail[mvif->idx] = skb;
 }
 
+static void
+mt76x2_resync_beacon_timer(struct mt76x2_dev *dev)
+{
+       u32 timer_val = dev->beacon_int << 4;
+
+       dev->tbtt_count++;
+
+       /*
+        * Beacon timer drifts by 1us every tick, the timer is configured
+        * in 1/16 TU (64us) units.
+        */
+       if (dev->tbtt_count < 62)
+               return;
+
+       if (dev->tbtt_count >= 64) {
+               dev->tbtt_count = 0;
+               return;
+       }
+
+       /*
+        * The updated beacon interval takes effect after two TBTT, because
+        * at this point the original interval has already been loaded into
+        * the next TBTT_TIMER value
+        */
+       if (dev->tbtt_count == 62)
+               timer_val -= 1;
+
+       mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
+                      MT_BEACON_TIME_CFG_INTVAL, timer_val);
+}
+
 void mt76x2_pre_tbtt_tasklet(unsigned long arg)
 {
        struct mt76x2_dev *dev = (struct mt76x2_dev *) arg;
@@ -226,6 +257,8 @@ void mt76x2_pre_tbtt_tasklet(unsigned long arg)
        struct sk_buff *skb;
        int i, nframes;
 
+       mt76x2_resync_beacon_timer(dev);
+
        data.dev = dev;
        __skb_queue_head_init(&data.q);