mac80211: handle SMPS action frames
authorJohannes Berg <johannes.berg@intel.com>
Fri, 16 Dec 2011 14:28:57 +0000 (15:28 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 19 Dec 2011 19:40:22 +0000 (14:40 -0500)
When a peer changes SMPS state we should update
rate control so it doesn't have to detect it by
itself. It can't detect "dynamic" mode anyway
since that just requires rts-cts handshaking.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/mac80211.h
net/mac80211/rx.c

index 5b5c8a7..2a7523e 100644 (file)
@@ -3502,9 +3502,12 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
  *
  * @IEEE80211_RC_HT_CHANGED: The HT parameters of the operating channel have
  *     changed, rate control algorithm can update its internal state if needed.
+ * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed, the rate
+ *     control algorithm needs to adjust accordingly.
  */
 enum rate_control_changed {
-       IEEE80211_RC_HT_CHANGED = BIT(0)
+       IEEE80211_RC_HT_CHANGED         = BIT(0),
+       IEEE80211_RC_SMPS_CHANGED       = BIT(1),
 };
 
 /**
index 2be5b7d..57832eb 100644 (file)
@@ -28,6 +28,7 @@
 #include "wpa.h"
 #include "tkip.h"
 #include "wme.h"
+#include "rate.h"
 
 /*
  * monitor mode reception
@@ -2233,6 +2234,63 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
                return RX_DROP_UNUSABLE;
 
        switch (mgmt->u.action.category) {
+       case WLAN_CATEGORY_HT:
+               /* reject HT action frames from stations not supporting HT */
+               if (!rx->sta->sta.ht_cap.ht_supported)
+                       goto invalid;
+
+               if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+                   sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+                   sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+                   sdata->vif.type != NL80211_IFTYPE_AP &&
+                   sdata->vif.type != NL80211_IFTYPE_ADHOC)
+                       break;
+
+               /* verify action & smps_control are present */
+               if (len < IEEE80211_MIN_ACTION_SIZE + 2)
+                       goto invalid;
+
+               switch (mgmt->u.action.u.ht_smps.action) {
+               case WLAN_HT_ACTION_SMPS: {
+                       struct ieee80211_supported_band *sband;
+                       u8 smps;
+
+                       /* convert to HT capability */
+                       switch (mgmt->u.action.u.ht_smps.smps_control) {
+                       case WLAN_HT_SMPS_CONTROL_DISABLED:
+                               smps = WLAN_HT_CAP_SM_PS_DISABLED;
+                               break;
+                       case WLAN_HT_SMPS_CONTROL_STATIC:
+                               smps = WLAN_HT_CAP_SM_PS_STATIC;
+                               break;
+                       case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+                               smps = WLAN_HT_CAP_SM_PS_DYNAMIC;
+                               break;
+                       default:
+                               goto invalid;
+                       }
+                       smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+                       /* if no change do nothing */
+                       if ((rx->sta->sta.ht_cap.cap &
+                                       IEEE80211_HT_CAP_SM_PS) == smps)
+                               goto handled;
+
+                       rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS;
+                       rx->sta->sta.ht_cap.cap |= smps;
+
+                       sband = rx->local->hw.wiphy->bands[status->band];
+
+                       rate_control_rate_update(local, sband, rx->sta,
+                                                IEEE80211_RC_SMPS_CHANGED,
+                                                local->_oper_channel_type);
+                       goto handled;
+               }
+               default:
+                       goto invalid;
+               }
+
+               break;
        case WLAN_CATEGORY_BACK:
                if (sdata->vif.type != NL80211_IFTYPE_STATION &&
                    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&