linux/net/mac80211/pm.c
<<
>>
Prefs
   1#include <net/mac80211.h>
   2#include <net/rtnetlink.h>
   3
   4#include "ieee80211_i.h"
   5#include "mesh.h"
   6#include "driver-ops.h"
   7#include "led.h"
   8
   9/* return value indicates whether the driver should be further notified */
  10static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
  11{
  12        switch (sdata->vif.type) {
  13        case NL80211_IFTYPE_STATION:
  14                ieee80211_sta_quiesce(sdata);
  15                return true;
  16        case NL80211_IFTYPE_ADHOC:
  17                ieee80211_ibss_quiesce(sdata);
  18                return true;
  19        case NL80211_IFTYPE_MESH_POINT:
  20                ieee80211_mesh_quiesce(sdata);
  21                return true;
  22        case NL80211_IFTYPE_AP_VLAN:
  23        case NL80211_IFTYPE_MONITOR:
  24                /* don't tell driver about this */
  25                return false;
  26        default:
  27                return true;
  28        }
  29}
  30
  31int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
  32{
  33        struct ieee80211_local *local = hw_to_local(hw);
  34        struct ieee80211_sub_if_data *sdata;
  35        struct sta_info *sta;
  36        struct ieee80211_chanctx *ctx;
  37
  38        if (!local->open_count)
  39                goto suspend;
  40
  41        ieee80211_scan_cancel(local);
  42
  43        if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
  44                mutex_lock(&local->sta_mtx);
  45                list_for_each_entry(sta, &local->sta_list, list) {
  46                        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
  47                        ieee80211_sta_tear_down_BA_sessions(sta, true);
  48                }
  49                mutex_unlock(&local->sta_mtx);
  50        }
  51
  52        ieee80211_stop_queues_by_reason(hw,
  53                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
  54
  55        /* flush out all packets */
  56        synchronize_net();
  57
  58        drv_flush(local, false);
  59
  60        local->quiescing = true;
  61        /* make quiescing visible to timers everywhere */
  62        mb();
  63
  64        flush_workqueue(local->workqueue);
  65
  66        /* Don't try to run timers while suspended. */
  67        del_timer_sync(&local->sta_cleanup);
  68
  69         /*
  70         * Note that this particular timer doesn't need to be
  71         * restarted at resume.
  72         */
  73        cancel_work_sync(&local->dynamic_ps_enable_work);
  74        del_timer_sync(&local->dynamic_ps_timer);
  75
  76        local->wowlan = wowlan && local->open_count;
  77        if (local->wowlan) {
  78                int err = drv_suspend(local, wowlan);
  79                if (err < 0) {
  80                        local->quiescing = false;
  81                        local->wowlan = false;
  82                        if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
  83                                mutex_lock(&local->sta_mtx);
  84                                list_for_each_entry(sta,
  85                                                    &local->sta_list, list) {
  86                                        clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
  87                                }
  88                                mutex_unlock(&local->sta_mtx);
  89                        }
  90                        ieee80211_wake_queues_by_reason(hw,
  91                                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
  92                        return err;
  93                } else if (err > 0) {
  94                        WARN_ON(err != 1);
  95                        local->wowlan = false;
  96                } else {
  97                        list_for_each_entry(sdata, &local->interfaces, list) {
  98                                cancel_work_sync(&sdata->work);
  99                                ieee80211_quiesce(sdata);
 100                        }
 101                        goto suspend;
 102                }
 103        }
 104
 105        /* disable keys */
 106        list_for_each_entry(sdata, &local->interfaces, list)
 107                ieee80211_disable_keys(sdata);
 108
 109        /* tear down aggregation sessions and remove STAs */
 110        mutex_lock(&local->sta_mtx);
 111        list_for_each_entry(sta, &local->sta_list, list) {
 112                if (sta->uploaded) {
 113                        enum ieee80211_sta_state state;
 114
 115                        state = sta->sta_state;
 116                        for (; state > IEEE80211_STA_NOTEXIST; state--)
 117                                WARN_ON(drv_sta_state(local, sta->sdata, sta,
 118                                                      state, state - 1));
 119                }
 120
 121                mesh_plink_quiesce(sta);
 122        }
 123        mutex_unlock(&local->sta_mtx);
 124
 125        /* remove all interfaces */
 126        list_for_each_entry(sdata, &local->interfaces, list) {
 127                cancel_work_sync(&sdata->work);
 128
 129                if (!ieee80211_quiesce(sdata))
 130                        continue;
 131
 132                if (!ieee80211_sdata_running(sdata))
 133                        continue;
 134
 135                /* disable beaconing */
 136                ieee80211_bss_info_change_notify(sdata,
 137                        BSS_CHANGED_BEACON_ENABLED);
 138
 139                if (sdata->vif.type == NL80211_IFTYPE_AP &&
 140                    rcu_access_pointer(sdata->u.ap.beacon))
 141                        drv_stop_ap(local, sdata);
 142
 143                if (local->use_chanctx) {
 144                        struct ieee80211_chanctx_conf *conf;
 145
 146                        mutex_lock(&local->chanctx_mtx);
 147                        conf = rcu_dereference_protected(
 148                                        sdata->vif.chanctx_conf,
 149                                        lockdep_is_held(&local->chanctx_mtx));
 150                        if (conf) {
 151                                ctx = container_of(conf,
 152                                                   struct ieee80211_chanctx,
 153                                                   conf);
 154                                drv_unassign_vif_chanctx(local, sdata, ctx);
 155                        }
 156
 157                        mutex_unlock(&local->chanctx_mtx);
 158                }
 159                drv_remove_interface(local, sdata);
 160        }
 161
 162        sdata = rtnl_dereference(local->monitor_sdata);
 163        if (sdata) {
 164                if (local->use_chanctx) {
 165                        struct ieee80211_chanctx_conf *conf;
 166
 167                        mutex_lock(&local->chanctx_mtx);
 168                        conf = rcu_dereference_protected(
 169                                        sdata->vif.chanctx_conf,
 170                                        lockdep_is_held(&local->chanctx_mtx));
 171                        if (conf) {
 172                                ctx = container_of(conf,
 173                                                   struct ieee80211_chanctx,
 174                                                   conf);
 175                                drv_unassign_vif_chanctx(local, sdata, ctx);
 176                        }
 177
 178                        mutex_unlock(&local->chanctx_mtx);
 179                }
 180
 181                drv_remove_interface(local, sdata);
 182        }
 183
 184        mutex_lock(&local->chanctx_mtx);
 185        list_for_each_entry(ctx, &local->chanctx_list, list)
 186                drv_remove_chanctx(local, ctx);
 187        mutex_unlock(&local->chanctx_mtx);
 188
 189        /* stop hardware - this must stop RX */
 190        if (local->open_count)
 191                ieee80211_stop_device(local);
 192
 193 suspend:
 194        local->suspended = true;
 195        /* need suspended to be visible before quiescing is false */
 196        barrier();
 197        local->quiescing = false;
 198
 199        return 0;
 200}
 201
 202/*
 203 * __ieee80211_resume() is a static inline which just calls
 204 * ieee80211_reconfig(), which is also needed for hardware
 205 * hang/firmware failure/etc. recovery.
 206 */
 207