From 687da132234feb70748df04a007bc1820f392254 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 1 Oct 2013 16:45:43 +0300 Subject: mac80211: implement SMPS for AP When the driver requests to move to STATIC or DYNAMIC SMPS, we send an action frame to each associated station and reconfigure the channel context / driver. Of course, non-MIMO stations are ignored. The beacon isn't updated. The association response will include the original capabilities. Stations that associate while in non-OFF SMPS mode will get an action frame right after association to inform them about our current state. Note that we wait until the end of the EAPOL. Sending an action frame before the EAPOL is finished can be an issue for a few clients. Clients aren't likely to send EAPOL frames in MIMO anyway. When the SMPS configuration gets more permissive (e.g. STATIC -> OFF), we don't wake up stations that are asleep We remember that they don't know about the change and send the action frame when they wake up. When the SMPS configuration gets more restrictive (e.g. OFF -> STATIC), we set the TIM bit for every sleeping STA. uAPSD stations might send MIMO until they poll the action frame, but this is for a short period of time. Signed-off-by: Emmanuel Grumbach [fix vht streams loop, initialisation] Signed-off-by: Johannes Berg --- net/mac80211/util.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'net/mac80211/util.c') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 550a6880625..b763e4ccaf5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2353,3 +2353,28 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) return ret; } + +/* + * Returns true if smps_mode_new is strictly more restrictive than + * smps_mode_old. + */ +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, + enum ieee80211_smps_mode smps_mode_new) +{ + if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || + smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) + return false; + + switch (smps_mode_old) { + case IEEE80211_SMPS_STATIC: + return false; + case IEEE80211_SMPS_DYNAMIC: + return smps_mode_new == IEEE80211_SMPS_STATIC; + case IEEE80211_SMPS_OFF: + return smps_mode_new != IEEE80211_SMPS_OFF; + default: + WARN_ON(1); + } + + return false; +} -- cgit v1.2.3-18-g5258 From 17ac49594fc574665e937f5804134087c0f37115 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 15 Oct 2013 16:16:21 -0500 Subject: mac80211: Remove check for offchannel state when waking netdev queues 6c17b77b67587b9f9e3070fb89fe98cef3187131 ensures that a device's mac80211 queues will remain stopped while offchannel. Since the vif can no longer be offchannel when the queues wake it's not necessary to check for this before waking its netdev queues. Signed-off-by: Seth Forshee Signed-off-by: Johannes Berg --- net/mac80211/util.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net/mac80211/util.c') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b763e4ccaf5..65ebe0c5e83 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) if (!sdata->dev) continue; - if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) - continue; - if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && local->queue_stop_reasons[sdata->vif.cab_queue] != 0) continue; -- cgit v1.2.3-18-g5258 From 8f2535b92d685c68db4bc699dd78462a646f6ef9 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Mon, 14 Oct 2013 19:08:27 -0700 Subject: mac80211: process the CSA frame for mesh accordingly Process the CSA frame according to the procedures define in IEEE Std 802.11-2012 section 10.9.8.4.3 as follow: * The mesh channel switch parameters element (MCSP) must be availabe. * If the MCSP's TTL is 1, drop the frame but still process the CSA. * If the MCSP's precedence value is less than or equal to the current precedence value, drop the frame and do not process the CSA. * The CSA frame is forwarded after TTL is decremented by 1 and the initiator field is set to 0. Transmit restrict field and others are maintained as is. * No beacon or probe response frame are handled here. Also, introduce the debug message used for mesh CSA purpose. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/util.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net/mac80211/util.c') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 65ebe0c5e83..523783cedf6 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -740,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, case WLAN_EID_TIMEOUT_INTERVAL: case WLAN_EID_SECONDARY_CHANNEL_OFFSET: case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + case WLAN_EID_CHAN_SWITCH_PARAM: /* * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible * that if the content gets bigger it might be needed more than once @@ -905,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, } elems->sec_chan_offs = (void *)pos; break; + case WLAN_EID_CHAN_SWITCH_PARAM: + if (elen != + sizeof(*elems->mesh_chansw_params_ie)) { + elem_parse_failed = true; + break; + } + elems->mesh_chansw_params_ie = (void *)pos; + break; case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: if (!action || elen != sizeof(*elems->wide_bw_chansw_ie)) { -- cgit v1.2.3-18-g5258 From c6da674aff9425dc41255bcb7f7586a656843f2d Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Mon, 14 Oct 2013 19:08:28 -0700 Subject: {nl,cfg,mac}80211: enable the triggering of CSA frame in mesh Allow the triggering of CSA frame using mesh interface. The rules are more or less same with IBSS, such as not allowed to change between the band and channel width has to be same from the previous mode. Also, move the ieee80211_send_action_csa to a common space so that it can be re-used by mesh interface. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/util.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'net/mac80211/util.c') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 523783cedf6..a38d58231af 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2384,3 +2384,90 @@ bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, return false; } + +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings) +{ + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + struct ieee80211_local *local = sdata->local; + int freq; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + + sizeof(mgmt->u.action.u.chan_switch); + u8 *pos; + + if (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + + 5 + /* channel switch announcement element */ + 3 + /* secondary channel offset element */ + 8); /* mesh channel switch parameters element */ + if (!skb) + return -ENOMEM; + + skb_reserve(skb, local->tx_headroom); + mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + eth_broadcast_addr(mgmt->da); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + if (ieee80211_vif_is_mesh(&sdata->vif)) { + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + } else { + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); + } + mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; + mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; + pos = skb_put(skb, 5); + *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ + *pos++ = 3; /* IE length */ + *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ + freq = csa_settings->chandef.chan->center_freq; + *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ + *pos++ = csa_settings->count; /* count */ + + if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { + enum nl80211_channel_type ch_type; + + skb_put(skb, 3); + *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ + *pos++ = 1; /* IE length */ + ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); + if (ch_type == NL80211_CHAN_HT40PLUS) + *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + } + + if (ieee80211_vif_is_mesh(&sdata->vif)) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + __le16 pre_value; + + skb_put(skb, 8); + *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ + *pos++ = 6; /* IE length */ + *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ + *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ + *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + *pos++ |= csa_settings->block_tx ? + WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; + put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ + pos += 2; + if (!ifmsh->pre_value) + ifmsh->pre_value = 1; + else + ifmsh->pre_value++; + pre_value = cpu_to_le16(ifmsh->pre_value); + memcpy(pos, &pre_value, 2); /* Precedence Value */ + pos += 2; + ifmsh->chsw_init = true; + } + + ieee80211_tx_skb(sdata, skb); + return 0; +} -- cgit v1.2.3-18-g5258