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
  37        if (!local->open_count)
  38                goto suspend;
  39
  40        ieee80211_scan_cancel(local);
  41
  42        if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
  43                mutex_lock(&local->sta_mtx);
  44                list_for_each_entry(sta, &local->sta_list, list) {
  45                        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
  46                        ieee80211_sta_tear_down_BA_sessions(sta, true);
  47                }
  48                mutex_unlock(&local->sta_mtx);
  49        }
  50
  51        ieee80211_stop_queues_by_reason(hw,
  52                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
  53
  54        /* flush out all packets */
  55        synchronize_net();
  56
  57        drv_flush(local, false);
  58
  59        local->quiescing = true;
  60        /* make quiescing visible to timers everywhere */
  61        mb();
  62
  63        flush_workqueue(local->workqueue);
  64
  65        /* Don't try to run timers while suspended. */
  66        del_timer_sync(&local->sta_cleanup);
  67
  68         /*
  69         * Note that this particular timer doesn't need to be
  70         * restarted at resume.
  71         */
  72        cancel_work_sync(&local->dynamic_ps_enable_work);
  73        del_timer_sync(&local->dynamic_ps_timer);
  74
  75        local->wowlan = wowlan && local->open_count;
  76        if (local->wowlan) {
  77                int err = drv_suspend(local, wowlan);
  78                if (err < 0) {
  79                        local->quiescing = false;
  80                        local->wowlan = false;
  81                        if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
  82                                mutex_lock(&local->sta_mtx);
  83                                list_for_each_entry(sta,
  84                                                    &local->sta_list, list) {
  85                                        clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
  86                                }
  87                                mutex_unlock(&local->sta_mtx);
  88                        }
  89                        ieee80211_wake_queues_by_reason(hw,
  90                                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
  91                        return err;
  92                } else if (err > 0) {
  93                        WARN_ON(err != 1);
  94                        local->wowlan = false;
  95                } else {
  96                        list_for_each_entry(sdata, &local->interfaces, list) {
  97                                cancel_work_sync(&sdata->work);
  98                                ieee80211_quiesce(sdata);
  99                        }
 100                        goto suspend;
 101                }
 102        }
 103
 104        /* disable keys */
 105        list_for_each_entry(sdata, &local->interfaces, list)
 106                ieee80211_disable_keys(sdata);
 107
 108        /* tear down aggregation sessions and remove STAs */
 109        mutex_lock(&local->sta_mtx);
 110        list_for_each_entry(sta, &local->sta_list, list) {
 111                if (sta->uploaded) {
 112                        enum ieee80211_sta_state state;
 113
 114                        state = sta->sta_state;
 115                        for (; state > IEEE80211_STA_NOTEXIST; state--)
 116                                WARN_ON(drv_sta_state(local, sta->sdata, sta,
 117                                                      state, state - 1));
 118                }
 119
 120                mesh_plink_quiesce(sta);
 121        }
 122        mutex_unlock(&local->sta_mtx);
 123
 124        /* remove all interfaces */
 125        list_for_each_entry(sdata, &local->interfaces, list) {
 126                cancel_work_sync(&sdata->work);
 127
 128                if (!ieee80211_quiesce(sdata))
 129                        continue;
 130
 131                if (!ieee80211_sdata_running(sdata))
 132                        continue;
 133
 134                /* disable beaconing */
 135                ieee80211_bss_info_change_notify(sdata,
 136                        BSS_CHANGED_BEACON_ENABLED);
 137
 138                drv_remove_interface(local, sdata);
 139        }
 140
 141        sdata = rtnl_dereference(local->monitor_sdata);
 142        if (sdata)
 143                drv_remove_interface(local, sdata);
 144
 145        /* stop hardware - this must stop RX */
 146        if (local->open_count)
 147                ieee80211_stop_device(local);
 148
 149 suspend:
 150        local->suspended = true;
 151        /* need suspended to be visible before quiescing is false */
 152        barrier();
 153        local->quiescing = false;
 154
 155        return 0;
 156}
 157
 158/*
 159 * __ieee80211_resume() is a static inline which just calls
 160 * ieee80211_reconfig(), which is also needed for hardware
 161 * hang/firmware failure/etc. recovery.
 162 */
 163
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.