mac80211: handle SMPS action frames
[linux-2.6-block.git] / net / mac80211 / rx.c
index 2be5b7d69ad71aea6c33e65e20e6441703ff8211..57832eb44f3e8f1abf6484ae3afa4aceb1cd33e3 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 &&