diff mbox

[5/7] mac80211: add wide bandwidth channel switch announcement to CSA action frames and mesh beacons

Message ID 20170516092316.15636-6-sw@simonwunderlich.de (mailing list archive)
State Changes Requested
Delegated to: Johannes Berg
Headers show

Commit Message

Simon Wunderlich May 16, 2017, 9:23 a.m. UTC
To support HT and VHT CSA, beacons and action frames must include the
corresponding IEs.

Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
 net/mac80211/ieee80211_i.h |  2 ++
 net/mac80211/mesh.c        | 47 ++++++++++++++++++++++++++++++++++++++++++++--
 net/mac80211/util.c        | 40 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 2 deletions(-)

Comments

Johannes Berg May 19, 2017, 11:33 a.m. UTC | #1
I've applied patches 1-4 now.

The subject is a bit long - I was going to change it to

    mac80211: mesh: support sending wide bandwidth CSA
    
    To support HT and VHT channel switch announcements, both beacons
    and action frames must include the corresponding IEs.

but:

> +		   2 + 2 + sizeof(struct
> ieee80211_wide_bw_chansw_ie) +
> +		   2 + sizeof(struct ieee80211_sec_chan_offs_ie) +

The "2 + 2" should have a comment - no that I'm even really sure that
you need the wrapper?

> -		pos = skb_put(skb, 13);
> -		memset(pos, 0, 13);

Removing that is nice - but why do you do this:

> +		bool have_secondary_chan_offset = false;
> +		bool have_wide_bandwidth_cs = false;
> +		int ie_len = 2 + sizeof(struct
> ieee80211_channel_sw_ie) +
> +			     2 + sizeof(struct
> ieee80211_mesh_chansw_params_ie);
> +
> +		switch (csa->settings.chandef.width) {
> +		case NL80211_CHAN_WIDTH_80:
> +		case NL80211_CHAN_WIDTH_80P80:
> +		case NL80211_CHAN_WIDTH_160:
> +			have_wide_bandwidth_cs = true;
> +			ie_len += 2 + 2 +
> +				  sizeof(struct
> ieee80211_wide_bw_chansw_ie);
> +			break;
> +		case NL80211_CHAN_WIDTH_40:
> +			have_secondary_chan_offset = true;
> +			ie_len += 2 + sizeof(struct
> ieee80211_sec_chan_offs_ie);
> +		default:
> +			break;
> +		}
> +		pos = skb_put(skb, ie_len);
> +		memset(pos, 0, ie_len);

I think having multiple calls to skb_put() would be better.

>  		*pos++ = WLAN_EID_CHANNEL_SWITCH;
>  		*pos++ = 3;
>  		*pos++ = 0x0;
> @@ -760,6 +781,28 @@ ieee80211_mesh_build_beacon(struct
> ieee80211_if_mesh *ifmsh)
>  		pos += 2;
>  		put_unaligned_le16(ifmsh->pre_value, pos);
>  		pos += 2;
> +
> +		if (have_secondary_chan_offset) {
> +			enum nl80211_channel_type ct;

You can have one here, and re-initialize pos.

> +			*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
> /* EID */
> +			*pos++ = 1;				 
>    /* len */
> +			ct = cfg80211_get_chandef_type(&csa-
> >settings.chandef);
> +			if (ct == NL80211_CHAN_HT40PLUS)
> +				*pos++ =
> IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
> +			else
> +				*pos++ =
> IEEE80211_HT_PARAM_CHA_SEC_BELOW;
> +		}
> +
> +		if (have_wide_bandwidth_cs) {
> +			struct cfg80211_chan_def *chandef;

and likewise here.

That'd also be safer in a way.

johannes
Simon Wunderlich May 19, 2017, 11:45 a.m. UTC | #2
Hi Johannes,

On Friday, May 19, 2017 1:33:37 PM CEST Johannes Berg wrote:
> I've applied patches 1-4 now.
> 
> The subject is a bit long - I was going to change it to
> 
>     mac80211: mesh: support sending wide bandwidth CSA
>     
>     To support HT and VHT channel switch announcements, both beacons
>     and action frames must include the corresponding IEs.
> 
> but:
> > +		   2 + 2 + sizeof(struct
> > ieee80211_wide_bw_chansw_ie) +
> > +		   2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
> 
> The "2 + 2" should have a comment - no that I'm even really sure that
> you need the wrapper?
> 

right, I'll add a comment. The spec says I need it, and if I understood the 
parsing function correctly it will only search for the wide bw IE when it finds 
the wrapper.

> > -		pos = skb_put(skb, 13);
> > -		memset(pos, 0, 13);
> 
> Removing that is nice - but why do you do this:
> > +		bool have_secondary_chan_offset = false;
> > +		bool have_wide_bandwidth_cs = false;
> > +		int ie_len = 2 + sizeof(struct
> > ieee80211_channel_sw_ie) +
> > +			     2 + sizeof(struct
> > ieee80211_mesh_chansw_params_ie);
> > +
> > +		switch (csa->settings.chandef.width) {
> > +		case NL80211_CHAN_WIDTH_80:
> > +		case NL80211_CHAN_WIDTH_80P80:
> > +		case NL80211_CHAN_WIDTH_160:
> > +			have_wide_bandwidth_cs = true;
> > +			ie_len += 2 + 2 +
> > +				  sizeof(struct
> > ieee80211_wide_bw_chansw_ie);
> > +			break;
> > +		case NL80211_CHAN_WIDTH_40:
> > +			have_secondary_chan_offset = true;
> > +			ie_len += 2 + sizeof(struct
> > ieee80211_sec_chan_offs_ie);
> > +		default:
> > +			break;
> > +		}
> > +		pos = skb_put(skb, ie_len);
> > +		memset(pos, 0, ie_len);
> 
> I think having multiple calls to skb_put() would be better.
> 

OK.

>[...]
> 
> and likewise here.
> 
> That'd also be safer in a way.

Understood, I can change it this way.

Thanks a lot for the feedback!
      Simon
Johannes Berg May 19, 2017, 11:51 a.m. UTC | #3
Hi,

> The spec says I need it, and if I understood the parsing function
> correctly it will only search for the wide bw IE when it finds the
> wrapper.

Yeah, I think there's some difference between action frames and beacons
though. You're only building the beacon here, so I think this is right.

johannes
diff mbox

Patch

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c960e4999380..e3a0b295c5ce 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2066,6 +2066,8 @@  u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
 u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
 			       const struct cfg80211_chan_def *chandef,
 			       u16 prot_mode, bool rifs_mode);
+u8 *ieee80211_ie_build_wide_bw_cs(u8 *pos,
+				  const struct cfg80211_chan_def *chandef);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
 			       u32 cap);
 u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 4807a5d77572..7c6593c0d453 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -690,6 +690,8 @@  ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 		   2 + sizeof(struct ieee80211_channel_sw_ie) +
 		   /* Mesh Channel Switch Parameters */
 		   2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
+		   2 + 2 + sizeof(struct ieee80211_wide_bw_chansw_ie) +
+		   2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
 		   2 + 8 + /* supported rates */
 		   2 + 3; /* DS params */
 	tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
@@ -736,8 +738,27 @@  ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 	rcu_read_lock();
 	csa = rcu_dereference(ifmsh->csa);
 	if (csa) {
-		pos = skb_put(skb, 13);
-		memset(pos, 0, 13);
+		bool have_secondary_chan_offset = false;
+		bool have_wide_bandwidth_cs = false;
+		int ie_len = 2 + sizeof(struct ieee80211_channel_sw_ie) +
+			     2 + sizeof(struct ieee80211_mesh_chansw_params_ie);
+
+		switch (csa->settings.chandef.width) {
+		case NL80211_CHAN_WIDTH_80:
+		case NL80211_CHAN_WIDTH_80P80:
+		case NL80211_CHAN_WIDTH_160:
+			have_wide_bandwidth_cs = true;
+			ie_len += 2 + 2 +
+				  sizeof(struct ieee80211_wide_bw_chansw_ie);
+			break;
+		case NL80211_CHAN_WIDTH_40:
+			have_secondary_chan_offset = true;
+			ie_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie);
+		default:
+			break;
+		}
+		pos = skb_put(skb, ie_len);
+		memset(pos, 0, ie_len);
 		*pos++ = WLAN_EID_CHANNEL_SWITCH;
 		*pos++ = 3;
 		*pos++ = 0x0;
@@ -760,6 +781,28 @@  ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 		pos += 2;
 		put_unaligned_le16(ifmsh->pre_value, pos);
 		pos += 2;
+
+		if (have_secondary_chan_offset) {
+			enum nl80211_channel_type ct;
+
+			*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
+			*pos++ = 1;				    /* len */
+			ct = cfg80211_get_chandef_type(&csa->settings.chandef);
+			if (ct == NL80211_CHAN_HT40PLUS)
+				*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+			else
+				*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+		}
+
+		if (have_wide_bandwidth_cs) {
+			struct cfg80211_chan_def *chandef;
+
+			*pos++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER; /* EID */
+			*pos++ = 5;				  /* len */
+			/* put sub IE */
+			chandef = &csa->settings.chandef;
+			pos = ieee80211_ie_build_wide_bw_cs(pos, chandef);
+		}
 	}
 	rcu_read_unlock();
 
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index ac9ac6c35594..d2e885cbfdf8 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2414,6 +2414,37 @@  u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
 	return pos + sizeof(struct ieee80211_ht_operation);
 }
 
+u8 *ieee80211_ie_build_wide_bw_cs(u8 *pos,
+				  const struct cfg80211_chan_def *chandef)
+{
+	*pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH;	/* EID */
+	*pos++ = 3;					/* IE length */
+	/* New channel width */
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_80:
+		*pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ;
+		break;
+	case NL80211_CHAN_WIDTH_160:
+		*pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ;
+		break;
+	case NL80211_CHAN_WIDTH_80P80:
+		*pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+		break;
+	default:
+		*pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT;
+	}
+
+	/* new center frequency segment 0 */
+	*pos++ = ieee80211_frequency_to_channel(chandef->center_freq1);
+	/* new center frequency segment 1 */
+	if (chandef->center_freq2)
+		*pos++ = ieee80211_frequency_to_channel(chandef->center_freq2);
+	else
+		*pos++ = 0;
+
+	return pos;
+}
+
 u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
 				const struct cfg80211_chan_def *chandef)
 {
@@ -2964,6 +2995,7 @@  int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
 			    5 + /* channel switch announcement element */
 			    3 + /* secondary channel offset element */
+			    5 + /* wide bandwidth channel switch announcement */
 			    8); /* mesh channel switch parameters element */
 	if (!skb)
 		return -ENOMEM;
@@ -3022,6 +3054,14 @@  int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 		pos += 2;
 	}
 
+	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_80 ||
+	    csa_settings->chandef.width == NL80211_CHAN_WIDTH_80P80 ||
+	    csa_settings->chandef.width == NL80211_CHAN_WIDTH_160) {
+		skb_put(skb, 5);
+		pos = ieee80211_ie_build_wide_bw_cs(pos,
+						    &csa_settings->chandef);
+	}
+
 	ieee80211_tx_skb(sdata, skb);
 	return 0;
 }