[PATCH] mac80211: add QBSS load element to beacons

Arthur Léna arthur at lena.im
Fri Aug 7 10:05:33 CEST 2015


The QBSS load element is specified in IEEE 802.11-2012 $8.4.2.30.
It is used to transmit population at a STA and a ratio of the
utilization of the operating channel. This implementation fills
the channel utilization field and the station count field. The
available admission capacity is always set to 0.
---
 net/mac80211/ieee80211_i.h |  7 +++++
 net/mac80211/main.c        |  3 ++
 net/mac80211/tx.c          | 70 ++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 36f217e..b4dacde 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -83,6 +83,8 @@ struct ieee80211_local;
 
 #define IEEE80211_DEAUTH_FRAME_LEN	(24 /* hdr */ + 2 /* reason */)
 
+#define DOT11_CHANNEL_UTILIZATION_BEACON_INTERVAL 50
+
 struct ieee80211_fragment_entry {
 	struct sk_buff_head skb_list;
 	unsigned long first_frag_time;
@@ -1354,6 +1356,11 @@ struct ieee80211_local {
 	/* TDLS channel switch */
 	struct work_struct tdls_chsw_work;
 	struct sk_buff_head skb_queue_tdls_chsw;
+
+	/* BSS load element */
+	u64 qbss_last_time_busy;
+	u32 qbss_interval_cnt;
+	u8 qbss_chan_util;
 };
 
 static inline struct ieee80211_sub_if_data *
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index ff79a13..f38f836 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -157,6 +157,9 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
 		local->hw.conf.power_level = power;
 	}
 
+	local->qbss_sending_interval_cnt = 0;
+	local->qbss_last_time_busy = 0;
+	local->qbss_chan_util = 0;
 	return changed;
 }
 
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2079d48..881618d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -3167,6 +3167,70 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
 	return 0;
 }
 
+static void ieee80211_beacon_add_qbss(struct ieee80211_sub_if_data *sdata,
+				       struct sk_buff *skb)
+{
+	struct ieee80211_local *local = sdata->local;
+	u8 *hdr;
+
+	if (++local->qbss_interval_cnt >=
+		DOT11_CHANNEL_UTILIZATION_BEACON_INTERVAL) {
+		struct ieee80211_supported_band *sband;
+		struct ieee80211_channel *current_chan;
+		struct ieee80211_chanctx_conf *chanctx_conf;
+		int band_idx, chan_idx = 0, idx = 0;
+		struct survey_info survey;
+		u64 busy_time;
+
+		rcu_read_lock();
+		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+		if (chanctx_conf)
+			current_chan = chanctx_conf->def.chan;
+		else
+			current_chan = NULL;
+		rcu_read_unlock();
+		if (!current_chan)
+			return;
+
+		for (band_idx = 0; band_idx < IEEE80211_NUM_BANDS; band_idx++) {
+			sband = local->hw.wiphy->bands[band_idx];
+			chan_idx += sband->n_channels;
+			while (idx < chan_idx &&
+			       current_chan != &sband->channels[idx])
+				idx++;
+			if (idx < chan_idx)
+				break;
+			band_idx++;
+		}
+		if (idx == chan_idx)
+			return;
+
+		memset(&survey, 0, sizeof(survey));
+		if (drv_get_survey(local, idx, &survey) != 0)
+			return;
+		if (!(survey.filled & SURVEY_INFO_TIME_BUSY))
+			return;
+		busy_time = survey.time_busy - local->qbss_last_time_busy;
+		local->qbss_chan_util = (u8)div64_u64(busy_time * 255,
+			local->qbss_interval_cnt *
+			sdata->vif.bss_conf.beacon_int);
+		local->qbss_interval_cnt = 0;
+		local->qbss_last_time_busy = survey.time_busy;
+	}
+	hdr = (u8 *)skb_put(skb, 7);
+	if (!hdr)
+		return;
+	*hdr++ = WLAN_EID_QBSS_LOAD;
+	*hdr++ = 5;
+	mutex_lock(&local->sta_mtx);
+	*((u16 *)hdr) = cpu_to_le16(local->num_sta);
+	mutex_unlock(&local->sta_mtx);
+	hdr += 2;
+	*hdr++ = local->qbss_last_time_busy ? local->qbss_chan_util : 255;
+	*hdr++ = 0;
+	*hdr++ = 0;
+}
+
 static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
 			      struct beacon_data *beacon)
 {
@@ -3352,7 +3416,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
 			 */
 			skb = dev_alloc_skb(local->tx_headroom +
 					    beacon->head_len +
-					    beacon->tail_len + 256 +
+					    beacon->tail_len + 256 + 7 +
 					    local->hw.extra_beacon_tailroom);
 			if (!skb)
 				goto out;
@@ -3364,6 +3428,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
 			ieee80211_beacon_add_tim(sdata, &ap->ps, skb,
 						 is_template);
 
+			ieee80211_beacon_add_qbss(sdata, skb);
 			if (offs) {
 				offs->tim_offset = beacon->head_len;
 				offs->tim_length = skb->len - beacon->head_len;
@@ -3392,13 +3457,14 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
 			ieee80211_set_csa(sdata, beacon);
 		}
 
-		skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
+		skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + 7 +
 				    local->hw.extra_beacon_tailroom);
 		if (!skb)
 			goto out;
 		skb_reserve(skb, local->tx_headroom);
 		memcpy(skb_put(skb, beacon->head_len), beacon->head,
 		       beacon->head_len);
+		ieee80211_beacon_add_qbss(sdata, skb);
 
 		hdr = (struct ieee80211_hdr *) skb->data;
 		hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-- 
2.4.6


--------------050803060603050305090704--


More information about the Battlemesh mailing list