linux/net/mac80211/ht.c
<<
>>
Prefs
   1/*
   2 * HT handling
   3 *
   4 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
   5 * Copyright 2002-2005, Instant802 Networks, Inc.
   6 * Copyright 2005-2006, Devicescape Software, Inc.
   7 * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
   8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
   9 * Copyright 2007-2010, Intel Corporation
  10 * Copyright 2017       Intel Deutschland GmbH
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License version 2 as
  14 * published by the Free Software Foundation.
  15 */
  16
  17#include <linux/ieee80211.h>
  18#include <linux/export.h>
  19#include <net/mac80211.h>
  20#include "ieee80211_i.h"
  21#include "rate.h"
  22
  23static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
  24                                  struct ieee80211_ht_cap *ht_capa_mask,
  25                                  struct ieee80211_sta_ht_cap *ht_cap,
  26                                  u16 flag)
  27{
  28        __le16 le_flag = cpu_to_le16(flag);
  29        if (ht_capa_mask->cap_info & le_flag) {
  30                if (!(ht_capa->cap_info & le_flag))
  31                        ht_cap->cap &= ~flag;
  32        }
  33}
  34
  35static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa,
  36                                  struct ieee80211_ht_cap *ht_capa_mask,
  37                                  struct ieee80211_sta_ht_cap *ht_cap,
  38                                  u16 flag)
  39{
  40        __le16 le_flag = cpu_to_le16(flag);
  41
  42        if ((ht_capa_mask->cap_info & le_flag) &&
  43            (ht_capa->cap_info & le_flag))
  44                ht_cap->cap |= flag;
  45}
  46
  47void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
  48                                     struct ieee80211_sta_ht_cap *ht_cap)
  49{
  50        struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
  51        u8 *scaps, *smask;
  52        int i;
  53
  54        if (!ht_cap->ht_supported)
  55                return;
  56
  57        switch (sdata->vif.type) {
  58        case NL80211_IFTYPE_STATION:
  59                ht_capa = &sdata->u.mgd.ht_capa;
  60                ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
  61                break;
  62        case NL80211_IFTYPE_ADHOC:
  63                ht_capa = &sdata->u.ibss.ht_capa;
  64                ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
  65                break;
  66        default:
  67                WARN_ON_ONCE(1);
  68                return;
  69        }
  70
  71        scaps = (u8 *)(&ht_capa->mcs.rx_mask);
  72        smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
  73
  74        /* NOTE:  If you add more over-rides here, update register_hw
  75         * ht_capa_mod_mask logic in main.c as well.
  76         * And, if this method can ever change ht_cap.ht_supported, fix
  77         * the check in ieee80211_add_ht_ie.
  78         */
  79
  80        /* check for HT over-rides, MCS rates first. */
  81        for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
  82                u8 m = smask[i];
  83                ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */
  84                /* Add back rates that are supported */
  85                ht_cap->mcs.rx_mask[i] |= (m & scaps[i]);
  86        }
  87
  88        /* Force removal of HT-40 capabilities? */
  89        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
  90                              IEEE80211_HT_CAP_SUP_WIDTH_20_40);
  91        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
  92                              IEEE80211_HT_CAP_SGI_40);
  93
  94        /* Allow user to disable SGI-20 (SGI-40 is handled above) */
  95        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
  96                              IEEE80211_HT_CAP_SGI_20);
  97
  98        /* Allow user to disable the max-AMSDU bit. */
  99        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
 100                              IEEE80211_HT_CAP_MAX_AMSDU);
 101
 102        /* Allow user to disable LDPC */
 103        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
 104                              IEEE80211_HT_CAP_LDPC_CODING);
 105
 106        /* Allow user to enable 40 MHz intolerant bit. */
 107        __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
 108                             IEEE80211_HT_CAP_40MHZ_INTOLERANT);
 109
 110        /* Allow user to decrease AMPDU factor */
 111        if (ht_capa_mask->ampdu_params_info &
 112            IEEE80211_HT_AMPDU_PARM_FACTOR) {
 113                u8 n = ht_capa->ampdu_params_info &
 114                       IEEE80211_HT_AMPDU_PARM_FACTOR;
 115                if (n < ht_cap->ampdu_factor)
 116                        ht_cap->ampdu_factor = n;
 117        }
 118
 119        /* Allow the user to increase AMPDU density. */
 120        if (ht_capa_mask->ampdu_params_info &
 121            IEEE80211_HT_AMPDU_PARM_DENSITY) {
 122                u8 n = (ht_capa->ampdu_params_info &
 123                        IEEE80211_HT_AMPDU_PARM_DENSITY)
 124                        >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
 125                if (n > ht_cap->ampdu_density)
 126                        ht_cap->ampdu_density = n;
 127        }
 128}
 129
 130
 131bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
 132                                       struct ieee80211_supported_band *sband,
 133                                       const struct ieee80211_ht_cap *ht_cap_ie,
 134                                       struct sta_info *sta)
 135{
 136        struct ieee80211_sta_ht_cap ht_cap, own_cap;
 137        u8 ampdu_info, tx_mcs_set_cap;
 138        int i, max_tx_streams;
 139        bool changed;
 140        enum ieee80211_sta_rx_bandwidth bw;
 141        enum ieee80211_smps_mode smps_mode;
 142
 143        memset(&ht_cap, 0, sizeof(ht_cap));
 144
 145        if (!ht_cap_ie || !sband->ht_cap.ht_supported)
 146                goto apply;
 147
 148        ht_cap.ht_supported = true;
 149
 150        own_cap = sband->ht_cap;
 151
 152        /*
 153         * If user has specified capability over-rides, take care
 154         * of that if the station we're setting up is the AP or TDLS peer that
 155         * we advertised a restricted capability set to. Override
 156         * our own capabilities and then use those below.
 157         */
 158        if (sdata->vif.type == NL80211_IFTYPE_STATION ||
 159            sdata->vif.type == NL80211_IFTYPE_ADHOC)
 160                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 161
 162        /*
 163         * The bits listed in this expression should be
 164         * the same for the peer and us, if the station
 165         * advertises more then we can't use those thus
 166         * we mask them out.
 167         */
 168        ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
 169                (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING |
 170                                 IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
 171                                 IEEE80211_HT_CAP_GRN_FLD |
 172                                 IEEE80211_HT_CAP_SGI_20 |
 173                                 IEEE80211_HT_CAP_SGI_40 |
 174                                 IEEE80211_HT_CAP_DSSSCCK40));
 175
 176        /*
 177         * The STBC bits are asymmetric -- if we don't have
 178         * TX then mask out the peer's RX and vice versa.
 179         */
 180        if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))
 181                ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
 182        if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))
 183                ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
 184
 185        ampdu_info = ht_cap_ie->ampdu_params_info;
 186        ht_cap.ampdu_factor =
 187                ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
 188        ht_cap.ampdu_density =
 189                (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
 190
 191        /* own MCS TX capabilities */
 192        tx_mcs_set_cap = own_cap.mcs.tx_params;
 193
 194        /* Copy peer MCS TX capabilities, the driver might need them. */
 195        ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
 196
 197        /* can we TX with MCS rates? */
 198        if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
 199                goto apply;
 200
 201        /* Counting from 0, therefore +1 */
 202        if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
 203                max_tx_streams =
 204                        ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
 205                                >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
 206        else
 207                max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;
 208
 209        /*
 210         * 802.11n-2009 20.3.5 / 20.6 says:
 211         * - indices 0 to 7 and 32 are single spatial stream
 212         * - 8 to 31 are multiple spatial streams using equal modulation
 213         *   [8..15 for two streams, 16..23 for three and 24..31 for four]
 214         * - remainder are multiple spatial streams using unequal modulation
 215         */
 216        for (i = 0; i < max_tx_streams; i++)
 217                ht_cap.mcs.rx_mask[i] =
 218                        own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
 219
 220        if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
 221                for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
 222                     i < IEEE80211_HT_MCS_MASK_LEN; i++)
 223                        ht_cap.mcs.rx_mask[i] =
 224                                own_cap.mcs.rx_mask[i] &
 225                                        ht_cap_ie->mcs.rx_mask[i];
 226
 227        /* handle MCS rate 32 too */
 228        if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
 229                ht_cap.mcs.rx_mask[32/8] |= 1;
 230
 231        /* set Rx highest rate */
 232        ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
 233
 234        if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
 235                sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
 236        else
 237                sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
 238
 239 apply:
 240        changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 241
 242        memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 243
 244        switch (sdata->vif.bss_conf.chandef.width) {
 245        default:
 246                WARN_ON_ONCE(1);
 247                /* fall through */
 248        case NL80211_CHAN_WIDTH_20_NOHT:
 249        case NL80211_CHAN_WIDTH_20:
 250                bw = IEEE80211_STA_RX_BW_20;
 251                break;
 252        case NL80211_CHAN_WIDTH_40:
 253        case NL80211_CHAN_WIDTH_80:
 254        case NL80211_CHAN_WIDTH_80P80:
 255        case NL80211_CHAN_WIDTH_160:
 256                bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
 257                                IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
 258                break;
 259        }
 260
 261        sta->sta.bandwidth = bw;
 262
 263        sta->cur_max_bandwidth =
 264                ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
 265                                IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
 266
 267        switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
 268                        >> IEEE80211_HT_CAP_SM_PS_SHIFT) {
 269        case WLAN_HT_CAP_SM_PS_INVALID:
 270        case WLAN_HT_CAP_SM_PS_STATIC:
 271                smps_mode = IEEE80211_SMPS_STATIC;
 272                break;
 273        case WLAN_HT_CAP_SM_PS_DYNAMIC:
 274                smps_mode = IEEE80211_SMPS_DYNAMIC;
 275                break;
 276        case WLAN_HT_CAP_SM_PS_DISABLED:
 277                smps_mode = IEEE80211_SMPS_OFF;
 278                break;
 279        }
 280
 281        if (smps_mode != sta->sta.smps_mode)
 282                changed = true;
 283        sta->sta.smps_mode = smps_mode;
 284
 285        return changed;
 286}
 287
 288void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
 289                                         enum ieee80211_agg_stop_reason reason)
 290{
 291        int i;
 292
 293        mutex_lock(&sta->ampdu_mlme.mtx);
 294        for (i = 0; i <  IEEE80211_NUM_TIDS; i++)
 295                ___ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
 296                                                WLAN_REASON_QSTA_LEAVE_QBSS,
 297                                                reason != AGG_STOP_DESTROY_STA &&
 298                                                reason != AGG_STOP_PEER_REQUEST);
 299
 300        for (i = 0; i <  IEEE80211_NUM_TIDS; i++)
 301                ___ieee80211_stop_tx_ba_session(sta, i, reason);
 302        mutex_unlock(&sta->ampdu_mlme.mtx);
 303
 304        /*
 305         * In case the tear down is part of a reconfigure due to HW restart
 306         * request, it is possible that the low level driver requested to stop
 307         * the BA session, so handle it to properly clean tid_tx data.
 308         */
 309        if(reason == AGG_STOP_DESTROY_STA) {
 310                cancel_work_sync(&sta->ampdu_mlme.work);
 311
 312                mutex_lock(&sta->ampdu_mlme.mtx);
 313                for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
 314                        struct tid_ampdu_tx *tid_tx =
 315                                rcu_dereference_protected_tid_tx(sta, i);
 316
 317                        if (!tid_tx)
 318                                continue;
 319
 320                        if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
 321                                ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
 322                }
 323                mutex_unlock(&sta->ampdu_mlme.mtx);
 324        }
 325}
 326
 327void ieee80211_ba_session_work(struct work_struct *work)
 328{
 329        struct sta_info *sta =
 330                container_of(work, struct sta_info, ampdu_mlme.work);
 331        struct tid_ampdu_tx *tid_tx;
 332        bool blocked;
 333        int tid;
 334
 335        /* When this flag is set, new sessions should be blocked. */
 336        blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA);
 337
 338        mutex_lock(&sta->ampdu_mlme.mtx);
 339        for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
 340                if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
 341                        ___ieee80211_stop_rx_ba_session(
 342                                sta, tid, WLAN_BACK_RECIPIENT,
 343                                WLAN_REASON_QSTA_TIMEOUT, true);
 344
 345                if (test_and_clear_bit(tid,
 346                                       sta->ampdu_mlme.tid_rx_stop_requested))
 347                        ___ieee80211_stop_rx_ba_session(
 348                                sta, tid, WLAN_BACK_RECIPIENT,
 349                                WLAN_REASON_UNSPECIFIED, true);
 350
 351                if (!blocked &&
 352                    test_and_clear_bit(tid,
 353                                       sta->ampdu_mlme.tid_rx_manage_offl))
 354                        ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
 355                                                         IEEE80211_MAX_AMPDU_BUF,
 356                                                         false, true);
 357
 358                if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS,
 359                                       sta->ampdu_mlme.tid_rx_manage_offl))
 360                        ___ieee80211_stop_rx_ba_session(
 361                                sta, tid, WLAN_BACK_RECIPIENT,
 362                                0, false);
 363
 364                spin_lock_bh(&sta->lock);
 365
 366                tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
 367                if (!blocked && tid_tx) {
 368                        /*
 369                         * Assign it over to the normal tid_tx array
 370                         * where it "goes live".
 371                         */
 372
 373                        sta->ampdu_mlme.tid_start_tx[tid] = NULL;
 374                        /* could there be a race? */
 375                        if (sta->ampdu_mlme.tid_tx[tid])
 376                                kfree(tid_tx);
 377                        else
 378                                ieee80211_assign_tid_tx(sta, tid, tid_tx);
 379                        spin_unlock_bh(&sta->lock);
 380
 381                        ieee80211_tx_ba_session_handle_start(sta, tid);
 382                        continue;
 383                }
 384                spin_unlock_bh(&sta->lock);
 385
 386                tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
 387                if (!tid_tx)
 388                        continue;
 389
 390                if (!blocked &&
 391                    test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
 392                        ieee80211_start_tx_ba_cb(sta, tid, tid_tx);
 393                if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))
 394                        ___ieee80211_stop_tx_ba_session(sta, tid,
 395                                                        AGG_STOP_LOCAL_REQUEST);
 396                if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
 397                        ieee80211_stop_tx_ba_cb(sta, tid, tid_tx);
 398        }
 399        mutex_unlock(&sta->ampdu_mlme.mtx);
 400}
 401
 402void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
 403                          const u8 *da, u16 tid,
 404                          u16 initiator, u16 reason_code)
 405{
 406        struct ieee80211_local *local = sdata->local;
 407        struct sk_buff *skb;
 408        struct ieee80211_mgmt *mgmt;
 409        u16 params;
 410
 411        skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
 412        if (!skb)
 413                return;
 414
 415        skb_reserve(skb, local->hw.extra_tx_headroom);
 416        mgmt = skb_put_zero(skb, 24);
 417        memcpy(mgmt->da, da, ETH_ALEN);
 418        memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
 419        if (sdata->vif.type == NL80211_IFTYPE_AP ||
 420            sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
 421            sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
 422                memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
 423        else if (sdata->vif.type == NL80211_IFTYPE_STATION)
 424                memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
 425        else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 426                memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
 427
 428        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 429                                          IEEE80211_STYPE_ACTION);
 430
 431        skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
 432
 433        mgmt->u.action.category = WLAN_CATEGORY_BACK;
 434        mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
 435        params = (u16)(initiator << 11);        /* bit 11 initiator */
 436        params |= (u16)(tid << 12);             /* bit 15:12 TID number */
 437
 438        mgmt->u.action.u.delba.params = cpu_to_le16(params);
 439        mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
 440
 441        ieee80211_tx_skb(sdata, skb);
 442}
 443
 444void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
 445                             struct sta_info *sta,
 446                             struct ieee80211_mgmt *mgmt, size_t len)
 447{
 448        u16 tid, params;
 449        u16 initiator;
 450
 451        params = le16_to_cpu(mgmt->u.action.u.delba.params);
 452        tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
 453        initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;
 454
 455        ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n",
 456                           mgmt->sa, initiator ? "initiator" : "recipient",
 457                           tid,
 458                           le16_to_cpu(mgmt->u.action.u.delba.reason_code));
 459
 460        if (initiator == WLAN_BACK_INITIATOR)
 461                __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
 462                                               true);
 463        else
 464                __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);
 465}
 466
 467enum nl80211_smps_mode
 468ieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps)
 469{
 470        switch (smps) {
 471        case IEEE80211_SMPS_OFF:
 472                return NL80211_SMPS_OFF;
 473        case IEEE80211_SMPS_STATIC:
 474                return NL80211_SMPS_STATIC;
 475        case IEEE80211_SMPS_DYNAMIC:
 476                return NL80211_SMPS_DYNAMIC;
 477        default:
 478                return NL80211_SMPS_OFF;
 479        }
 480}
 481
 482int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 483                               enum ieee80211_smps_mode smps, const u8 *da,
 484                               const u8 *bssid)
 485{
 486        struct ieee80211_local *local = sdata->local;
 487        struct sk_buff *skb;
 488        struct ieee80211_mgmt *action_frame;
 489
 490        /* 27 = header + category + action + smps mode */
 491        skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
 492        if (!skb)
 493                return -ENOMEM;
 494
 495        skb_reserve(skb, local->hw.extra_tx_headroom);
 496        action_frame = skb_put(skb, 27);
 497        memcpy(action_frame->da, da, ETH_ALEN);
 498        memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
 499        memcpy(action_frame->bssid, bssid, ETH_ALEN);
 500        action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 501                                                  IEEE80211_STYPE_ACTION);
 502        action_frame->u.action.category = WLAN_CATEGORY_HT;
 503        action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
 504        switch (smps) {
 505        case IEEE80211_SMPS_AUTOMATIC:
 506        case IEEE80211_SMPS_NUM_MODES:
 507                WARN_ON(1);
 508                /* fall through */
 509        case IEEE80211_SMPS_OFF:
 510                action_frame->u.action.u.ht_smps.smps_control =
 511                                WLAN_HT_SMPS_CONTROL_DISABLED;
 512                break;
 513        case IEEE80211_SMPS_STATIC:
 514                action_frame->u.action.u.ht_smps.smps_control =
 515                                WLAN_HT_SMPS_CONTROL_STATIC;
 516                break;
 517        case IEEE80211_SMPS_DYNAMIC:
 518                action_frame->u.action.u.ht_smps.smps_control =
 519                                WLAN_HT_SMPS_CONTROL_DYNAMIC;
 520                break;
 521        }
 522
 523        /* we'll do more on status of this frame */
 524        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 525        ieee80211_tx_skb(sdata, skb);
 526
 527        return 0;
 528}
 529
 530void ieee80211_request_smps_mgd_work(struct work_struct *work)
 531{
 532        struct ieee80211_sub_if_data *sdata =
 533                container_of(work, struct ieee80211_sub_if_data,
 534                             u.mgd.request_smps_work);
 535
 536        sdata_lock(sdata);
 537        __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
 538        sdata_unlock(sdata);
 539}
 540
 541void ieee80211_request_smps_ap_work(struct work_struct *work)
 542{
 543        struct ieee80211_sub_if_data *sdata =
 544                container_of(work, struct ieee80211_sub_if_data,
 545                             u.ap.request_smps_work);
 546
 547        sdata_lock(sdata);
 548        if (sdata_dereference(sdata->u.ap.beacon, sdata))
 549                __ieee80211_request_smps_ap(sdata,
 550                                            sdata->u.ap.driver_smps_mode);
 551        sdata_unlock(sdata);
 552}
 553
 554void ieee80211_request_smps(struct ieee80211_vif *vif,
 555                            enum ieee80211_smps_mode smps_mode)
 556{
 557        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 558
 559        if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
 560                         vif->type != NL80211_IFTYPE_AP))
 561                return;
 562
 563        if (vif->type == NL80211_IFTYPE_STATION) {
 564                if (sdata->u.mgd.driver_smps_mode == smps_mode)
 565                        return;
 566                sdata->u.mgd.driver_smps_mode = smps_mode;
 567                ieee80211_queue_work(&sdata->local->hw,
 568                                     &sdata->u.mgd.request_smps_work);
 569        } else {
 570                /* AUTOMATIC is meaningless in AP mode */
 571                if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
 572                        return;
 573                if (sdata->u.ap.driver_smps_mode == smps_mode)
 574                        return;
 575                sdata->u.ap.driver_smps_mode = smps_mode;
 576                ieee80211_queue_work(&sdata->local->hw,
 577                                     &sdata->u.ap.request_smps_work);
 578        }
 579}
 580/* this might change ... don't want non-open drivers using it */
 581EXPORT_SYMBOL_GPL(ieee80211_request_smps);
 582