]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/util.c
mac80211: add helper functions for tracking P2P NoA state
[~andy/linux] / net / mac80211 / util.c
index ed93504d24b88d087dee1f5368308659043b5a19..df00f1978a77574857a9e8cf5c37cad66ce3e487 100644 (file)
@@ -2594,3 +2594,143 @@ int ieee80211_cs_headroom(struct ieee80211_local *local,
 
        return headroom;
 }
+
+static bool
+ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
+{
+       s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1);
+       int skip;
+
+       if (end > 0)
+               return false;
+
+       /* End time is in the past, check for repetitions */
+       skip = DIV_ROUND_UP(-end, data->desc[i].interval);
+       if (data->count[i] < 255) {
+               if (data->count[i] <= skip) {
+                       data->count[i] = 0;
+                       return false;
+               }
+
+               data->count[i] -= skip;
+       }
+
+       data->desc[i].start += skip * data->desc[i].interval;
+
+       return true;
+}
+
+static bool
+ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf,
+                            s32 *offset)
+{
+       bool ret = false;
+       int i;
+
+       for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
+               s32 cur;
+
+               if (!data->count[i])
+                       continue;
+
+               if (ieee80211_extend_noa_desc(data, tsf + *offset, i))
+                       ret = true;
+
+               cur = data->desc[i].start - tsf;
+               if (cur > *offset)
+                       continue;
+
+               cur = data->desc[i].start + data->desc[i].duration - tsf;
+               if (cur > *offset)
+                       *offset = cur;
+       }
+
+       return ret;
+}
+
+static u32
+ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf)
+{
+       s32 offset = 0;
+       int tries = 0;
+       /*
+        * arbitrary limit, used to avoid infinite loops when combined NoA
+        * descriptors cover the full time period.
+        */
+       int max_tries = 5;
+
+       ieee80211_extend_absent_time(data, tsf, &offset);
+       do {
+               if (!ieee80211_extend_absent_time(data, tsf, &offset))
+                       break;
+
+               tries++;
+       } while (tries < max_tries);
+
+       return offset;
+}
+
+void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf)
+{
+       u32 next_offset = BIT(31) - 1;
+       int i;
+
+       data->absent = 0;
+       data->has_next_tsf = false;
+       for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
+               s32 start;
+
+               if (!data->count[i])
+                       continue;
+
+               ieee80211_extend_noa_desc(data, tsf, i);
+               start = data->desc[i].start - tsf;
+               if (start <= 0)
+                       data->absent |= BIT(i);
+
+               if (next_offset > start)
+                       next_offset = start;
+
+               data->has_next_tsf = true;
+       }
+
+       if (data->absent)
+               next_offset = ieee80211_get_noa_absent_time(data, tsf);
+
+       data->next_tsf = tsf + next_offset;
+}
+EXPORT_SYMBOL(ieee80211_update_p2p_noa);
+
+int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
+                           struct ieee80211_noa_data *data, u32 tsf)
+{
+       int ret = 0;
+       int i;
+
+       memset(data, 0, sizeof(*data));
+
+       for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) {
+               const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i];
+
+               if (!desc->count || !desc->duration)
+                       continue;
+
+               data->count[i] = desc->count;
+               data->desc[i].start = le32_to_cpu(desc->start_time);
+               data->desc[i].duration = le32_to_cpu(desc->duration);
+               data->desc[i].interval = le32_to_cpu(desc->interval);
+
+               if (data->count[i] > 1 &&
+                   data->desc[i].interval < data->desc[i].duration)
+                       continue;
+
+               ieee80211_extend_noa_desc(data, tsf, i);
+               ret++;
+       }
+
+       if (ret)
+               ieee80211_update_p2p_noa(data, tsf);
+
+       return ret;
+}
+EXPORT_SYMBOL(ieee80211_parse_p2p_noa);