|
@@ -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);
|